Какова цель привязки ссылки класса к ковариантному типу?

Я играю с размышлениями, и я вышел с этой проблемой. При использовании ссылки связанного класса с помощью синтаксиса ::class я получаю ковариантный тип KClass:

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

Как я мог бы узнать из документов, это вернет точный тип объекта, если это экземпляр подтипа T , следовательно, модификатор дисперсии. Однако это предотвращает получение свойств, объявленных в классе T и получение их значения (это то, что я пытаюсь сделать)

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

Я обнаружил, что решение использует javaClass.kotlin расширения javaClass.kotlin на объект, чтобы вместо этого ввести тип инварианта:

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

Таким образом, я получаю как точный тип во время выполнения, так и возможность использовать тип.

Интересно, что если я использую супертип вместо общего, то с последним методом я все же получаю доступ к правильному типу без необходимости в дисперсии:

 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' } 

Если я правильно понял, ::class равен вызову java getClass , который возвращает тип варианта с подстановочным знаком, тогда как javaClass – это getClass с приведением к определенному типу. Тем не менее, я не понимаю, почему мне понадобится ковариантный KClass, когда он ограничивает меня только производством типа, учитывая, что есть другие способы доступа к точному классу во время выполнения и использование его свободно, и мне интересно, непосредственный ::class должен возвращать тип инварианта по дизайну.

Причиной ковариации в ссылках bound ::class является то, что фактический тип времени выполнения объекта, выражение которого оценивается, может отличаться от объявленного или предполагаемого типа выражения.

Пример:

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

Выражение someBase() набирается как Base , но во время выполнения это объект Derived которому он получает оценку.

Ввод someBase()::class качестве инварианта KClass<Base> просто неверен, на самом деле итоговым результатом оценки этого выражения является KClass<Derived> .

Чтобы решить эту возможную несогласованность (что приведет к нарушенной безопасности типов), все связанные ссылки на классы являются ковариантными: someBase()::class – это KClass<out Base> , что означает, что во время выполнения someBase() может быть подтипом Base , и поэтому это может быть признаком класса подтипа Base .

Это, конечно же, не относится к ссылкам с несвязанными классами: когда вы берете Base::class , вы точно знаете, что это маркер класса Base а не некоторые его подтипы, поэтому он является инвариантным KClass<Base> .