绑定类引用返回一个协变类型的目的是什么?
我正在玩反思,我出来解决这个问题。 当通过::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
扩展函数,来取得不变的类型:
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
是一个具有特定类型javaClass
的getClass
。 尽管如此,我还是不明白为什么我需要一个协变的KClass,当它限制我只产生这个类型的时候,考虑到在运行时还有其他方法可以访问这个类,并且可以自由地使用它,我想知道更多immediate ::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>
。