Herencia de Kotlin con generics

Tengo una class abstracta, llamémosla A.

abstract class A(private val name: String) { fun read(key: String): Entity { ... } fun write(entity: Entity) { ... } abstract val mapper: Mapper<Any> ... interface Mapper<T> { fun toEntity(entry: T): Entity fun fromEntity(entity: Entity): T } ... 

Tiene un mapeador abstracto. El objective de esto es que puedo asignar diferentes objects a Entity y usar read y write .

Mi class hija, llamémosla B, está estructurada así:

 class B(private val name: String) : A(name) { override val mapper = AnimalMapper object AnimalMapper: Mapper<Animal> { override fun fromEntity(entity: Entity): Animal { TODO("not implemented") } override fun toEntity(animal: Animal): Entity { TODO("not implemented") } } } 

Idealmente, quería tener una interfaz en el Mapper genérica y no en Any , pero estoy simplificando esto solo para la pregunta.

El problema es que recibo este error:

Type of 'mapper' is not a subtype of the overridden property 'public abstract val mapper: Mapper<Any> defined in ...

¿Porqué es eso?

Tenga en count los dos hechos sobre la inheritance y los generics:

  • Una propiedad val solo puede anularse con un subtipo del tipo de propiedad original. Esto se debe a que todos los usuarios del tipo esperan que devuelva alguna instancia del tipo original. Por ejemplo, está bien anular una propiedad CharSequence con String .

    Una propiedad var no puede usar un subtipo, solo el tipo original, ya que los usuarios pueden querer asignar una instancia del tipo original a la propiedad.

  • Los generics de Kotlin son invariables por defecto . Dado Mapper<T> , dos de sus instancias Mapper<A> y Mapper<B> no son subtypes entre sí si A y B son diferentes.

Dado esto, no puede anular una propiedad de tipo Mapper<Any> con Mapper<SomeType> , porque este último no es un subtipo del anterior.

No puede usar la varianza del sitio de statement para hacer que todos Mapper<T> usos de Mapper<T> covariantes (declare la interfaz como interface Mapper<out T> ) debido a que T usa como tipo de parámetro en fun toEntity(entry: T): Entity .

Puede intentar aplicar la varianza del sitio de uso , declarando la propiedad como

 abstract val mapper: Mapper<out Any> 

Pero de esta forma los usuarios de la class A no podrán llamar a fun toEntity(entry: T): Entity , ya que no sabrán cuál es el tipo real que reemplaza a Any en la class child y por lo tanto lo que pueden pasar con security como entry . Sin embargo, si el usuario conoce el tipo exacto (por ejemplo, B ), verá el tipo de mapper tal como se declara en la propiedad anulada.


Un patrón común que puede permitirle usar la propiedad anulada de forma más flexible es parametrizar la class de class A<T> y definir la propiedad como val mapper: Mapper<T> .

De esta forma, los subtypes deberán especificar qué T utilizan en su statement: class B(...) : A<Animal>(...) , y los usuarios que ven A<Animal> (incluso sin saber que es realmente B<Animal> obtendrá su mapper forma segura como Mapper<Animal> .

  • ¿Cómo comstackr y usar el código de Kotlin en time de ejecución?
  • Se produce una exception durante Evaluar acción de expresión:
  • Modificar la plantilla de file de testing
  • ¿Es posible el cruce de intersecciones en Kotlin?
  • Regresar la function de object de llamada de class interna
  • Solo la primera testing pasa con TestScheduler cuando se ejecutan varias testings (Kotlin)
  • kotlin consiguiendo un suscriptor para observar un observable usando RxJava2
  • Afirmación no nula de Kotlin en nulo
  • Intentando escribir un código eficiente para actualizar el color de background usando Kotlin
  • Obtuvo NoClassDefFoundError al pasar el map de lambda
  • Dibuja una línea en el canvas lentamente como una animation