Полиморфизм на функциях расширения в Котлине

У меня есть несколько классов, которые я не контролирую , на которых я уже создал несколько одинаково названных методов расширения в нескольких общих «атрибутах». Идентифицированные функции расширения всегда возвращают один и тот же тип значения, хотя и рассчитаны по-разному для каждого типа приемника. Ниже приведен упрощенный пример, основанный на встроенных типах только для одного атрибута:

// **DOES NOT COMPILE** // three sample classes I don't control extended for .len inline val String.len get() = length inline val <T> List<T>.len get() = size inline val <T> Sequence<T>.len get() = count() // another class which needs to act on things with .len class Calc<T>(val obj:T) { // HERE IS THE PROBLEM... val dbl get() = obj?.len * 2 // dummy property that doubles len // ... and other methods that use .len and other parallel extensions } fun main(a:Array<String>) { val s = "abc" val l = listOf(5,6,7) val q = (10..20 step 2).asSequence() val cs = Calc(s) val cl = Calc(l) val cq = Calc(q) println("Lens: ${cs.dbl}, ${cl.dbl}, ${cq.dbl}") } 

Представьте себе несколько других «общих» свойств, расширенных таким же образом, как и в некоторых классах, которые я не контролирую. Если я не хочу повторять себя в каждом классе, как мне создать правильно типизированный класс, который может работать с .len (и другими такими свойствами) в общем случае для этих трех классов?

Я изучил следующие, но не нашел работоспособных решений:

  • generics, в приведенном выше примере, но не может получить синтаксис правильно.
  • запечатанных классов, но я не контролирую эти классы.
  • типы соединений, которые я нашел, не поддерживаются в Котлине.
  • классов оболочки, но не смог получить синтаксис правильно.
  • пропуская lambdas a la это объяснение в блоге , но не получилось правильно, и казалось, что boptimalsu пропускает несколько лямбда вокруг каждого метода.

Должен быть лучший способ, не так ли?

Solutions Collecting From Web of "Полиморфизм на функциях расширения в Котлине"

Вот пример с закрытыми классами и одним свойством расширения, чтобы преобразовать что-либо в нечто, что может дать вам len или double . Не уверен, что он имеет лучшую читаемость.

 val Any?.calc get() = when(this) { is String -> Calc.CalcString(this) is List<*> -> Calc.CalcList(this) is Sequence<*> -> Calc.CalcSequense(this) else -> Calc.None } /* or alternatively without default fallback */ val String.calc get() = Calc.CalcString(this) val List<*>.calc get() = Calc.CalcList(this) val Sequence<*>.calc get() = Calc.CalcSequense(this) /* sealed extension classes */ sealed class Calc { abstract val len: Int? val dbl: Int? by lazy(LazyThreadSafetyMode.NONE) { len?.let { it * 2 } } class CalcString(val s: String): Calc() { override val len: Int? get() = s.length } class CalcList<out T>(val l: List<T>): Calc() { override val len: Int? get() = l.size } class CalcSequense<out T>(val s: Sequence<T>): Calc() { override val len: Int? get() = s.count() } object None: Calc() { override val len: Int? get() = null } } fun main(args: Array<String>) { val s = "abc".calc val l = listOf(5,6,7).calc val q = (10..20 step 2).asSequence().calc println("Lens: ${s.dbl}, ${l.dbl}, ${q.dbl}") }