¿Cuál es el propósito de que la reference de class enlazada devuelva un tipo covariante?

Estoy jugando con la reflexión y salí con este problema. Cuando uso una reference de class enlazada a través de la syntax ::class , obtengo un tipo de class KCovariante:

 fun <T> foo(entry: T) { with(entry::class) { this // is instance of KClass<out T> } } 

Como podría aprender de los documentos, esto devolverá el tipo exacto del object, en caso de que sea instancia de un subtipo de T , de ahí el modificador de varianza. Sin embargo, esto impide recuperar las properties declaradas en la class T y get su valor (que es lo que bash hacer)

 fun <T> foo(entry: T) { with(entry::class) { for (prop in memberProperties) { val v = prop.get(entry) //compile error: I can't consume T } } } 

Encontré que una solución está usando la function de extensión javaClass.kotlin en la reference del object, para get en su lugar el tipo invariante:

 fun <T> foo(entry: T) { with(entry.javaClass.kotlin) { this // is instance of KClass<T> } } 

De esta forma, obtengo el tipo exacto en time de ejecución y la posibilidad de consumir el tipo.

Curiosamente, si utilizo un supertipo en lugar de un genérico, con este último método aún tengo acceso al tipo correcto, sin la necesidad de varianza:

 class Derived: Base() fun foo(entry: Base) { with(entry.javaClass.kotlin) { println(this == Derived::class) } } fun main(args: Array<String>) { val derived = Derived() foo(derived) // prints 'true' } 

Si lo tengo correcto, ::class es igual a llamar a la getClass Java, que devuelve un tipo de variante con un comodín, mientras que javaClass es una getClass con un molde para el tipo específico. Aún así, no entiendo por qué necesitaría alguna vez una KClass covariante, cuando me limita a producir solo el tipo, dado que hay otras forms de acceder a la class exacta en time de ejecución y usarla libremente, y me pregunto si habrá más. immediate ::class debe devolver un tipo invariante por layout.

El motivo de la covarianza en ::class references de bound ::class es que el tipo de time de ejecución real de un object al que se evalúa la expresión puede diferir del tipo declarado o inferido de la expresión.

Ejemplo:

 open class Base class Derived : Base() fun someBase(): Base = Derived() val kClass = someBase()::class 

La expresión someBase() se escribe como Base , pero en el time de ejecución es un object Derived que se evalúa.

Escribir someBase()::class como invariante KClass<Base> es simplemente incorrecto, de hecho, el resultado KClass<Derived> de evaluar esta expresión es KClass<Derived> .

Para resolver esta posible incoinheritance (que daría lugar a security de tipo roto), todas las references de class enlazadas son covariantes: someBase()::class es KClass<out Base> , lo que significa que en time de ejecución someBase() podría ser un subtipo de Base , y por lo tanto, esto podría ser una ficha de class de un subtipo de Base .

Esto, por supuesto, no es el caso con las references de classs independientes: cuando se toma Base::class , se puede estar seguro de que es el token de class de Base y no de algunos de sus subtypes, por lo que es invariable KClass<Base> .

  • Compruebe si la class es un valor válido para KParameter
  • Combinar / fusionar classs de datos en Kotlin
  • Clase de datos KotlinReflectionInternalError