绑定类引用返回协变类型的目的是什么?

dev*_*cca 5 kotlin kotlin-reflect

我正在玩反思,我出现了这个问题.通过::class语法使用绑定类引用时,我得到一个协变的KClass类型:

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

正如我可以从文档中学习的那样,这将返回对象的确切类型,以防它是子类型的实例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
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

我发现一个解决方案是javaClass.kotlin在对象引用上使用扩展函数,而不是使用不变类型:

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

这样,我在运行时获得了确切的类型,并且可以使用该类型.

有趣的是,如果我使用超类型而不是泛型,使用后一种方法我仍然可以访问正确的类型,而不需要方差:

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'
}
Run Code Online (Sandbox Code Playgroud)

如果我得到了它正确的,::class等于调用Java getClass,它会返回一个通配符变量类型,同时javaClass是一个getClass具有强制转换为特定类型.仍然,我不明白为什么我需要一个协变KClass,当它限制我只生成类型,因为有其他方法可以在运行时访问确切的类并自由使用它,我想知道是否更多立即::class应该按设计返回不变类型.

hot*_*key 3

绑定引用中存在协变的原因::class是,表达式求值的对象的实际运行时类型可能与表达式的声明或推断类型不同。

例子:

open class Base
class Derived : Base()

fun someBase(): Base = Derived()

val kClass = someBase()::class
Run Code Online (Sandbox Code Playgroud)

该表达式someBase()的类型为Base,但在运行时它是一个Derived被求值的对象。

输入someBase()::class为 invariantKClass<Base>是完全不正确的,事实上,计算该表达式的实际结果是KClass<Derived>

为了解决这种可能的不一致(这将导致破坏类型安全),所有绑定类引用都是协变的:someBase()::classis KClass<out Base>,这意味着在运行时someBase()可能是 的子类型Base,因此这可能是 的子类型的类标记Base

当然,未绑定类引用的情况并非如此:当您采用 时Base::class,您可以确定它是 的类标记,Base而不是其某些子类型的类标记,因此它是不变的KClass<Base>