Polimorfismo en las funciones de extensión en Kotlin

Tengo varias classs que no controlo , sobre las cuales ya he creado varios methods de extensión con nombres idénticos en varios "attributes" comunes. Las funciones de extensión con nombre idéntico siempre devuelven el mismo tipo de valor, aunque se calculan de diferentes maneras para cada tipo de receptor. Aquí hay un ejemplo simplificado basado en types incorporados para un solo atributo:

// **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}") } 

Imagina varias otras properties "comunes" extendidas de la misma manera que .len en algunas classs que no controlo. Si no quiero repetirme en cada class, ¿cómo puedo build una class correctamente tipada que pueda operar en .len (y otras properties similares) genéricamente para estas tres classs?

Investigué las siguientes soluciones pero aún no se han encontrado soluciones viables:

  • generics, en el ejemplo anterior, pero no pueden get la syntax correcta.
  • classs selladas, pero no tengo control de estas classs.
  • types de unión, que he encontrado no son compatibles con Kotlin.
  • classs contenedoras, pero no pudieron get la syntax correcta.
  • pasando lambdas a la explicación de este blog , pero no lo hizo bien, y me pareció boptimalsu pasar múltiples lambdas por cada método.

Debe haber una mejor manera, ¿verdad?

Aquí hay un ejemplo con classs selladas y una propiedad de extensión única para convertir cualquier cosa a algo que puede darte len o double . No estoy seguro de si tiene mejor legibilidad.

 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}") }