Kotlin 绑定类引用返回协变类型的目的是什么?
我在玩反思游戏,我解决了这个问题。当通过Kotlin 绑定类引用返回协变类型的目的是什么?,kotlin,kotlin-reflect,Kotlin,Kotlin Reflect,我在玩反思游戏,我解决了这个问题。当通过::class语法使用绑定类引用时,我得到一个协变的KClass类型: fun <T> foo(entry: T) { with(entry::class) { this // is instance of KClass<out T> } } 我发现一个解决方案是在对象引用上使用javaClass.kotlin扩展函数,以获得不变类型: fun <T> foo(entry: T) {
::class
语法使用绑定类引用时,我得到一个协变的KClass类型:
fun <T> foo(entry: T) {
with(entry::class) {
this // is instance of KClass<out T>
}
}
我发现一个解决方案是在对象引用上使用javaClass.kotlin
扩展函数,以获得不变类型:
fun <T> foo(entry: T) {
with(entry.javaClass.kotlin) {
this // is instance of KClass<T>
}
}
fun-foo(条目:T){
使用(entry.javaClass.kotlin){
这是KClass的实例
}
}
通过这种方式,我可以在运行时获得确切的类型以及使用该类型的可能性
有趣的是,如果我使用超类型而不是泛型,使用后一种方法,我仍然可以访问正确的类型,而不需要变化:
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'
}
派生类:Base()
fun foo(参赛作品:Base){
使用(entry.javaClass.kotlin){
println(this==派生::类)
}
}
趣味主线(args:Array){
val派生=派生()
foo(派生)//打印“true”
}
如果我理解正确,::class
等于调用javagetClass
,它返回一个带有通配符的变量类型,而javaClass
是一个带有特定类型转换的getClass
。
尽管如此,我还是不明白为什么我需要协变KClass,因为它限制我只生成类型,因为有其他方法可以在运行时访问确切的类并自由使用它,我想知道更直接的
::class
是否应该通过设计返回一个不变类型。在bound::class
引用中产生协方差的原因是,表达式计算到的对象的实际运行时类型可能与表达式的声明或推断类型不同
例如:
open class Base
class Derived : Base()
fun someBase(): Base = Derived()
val kClass = someBase()::class
表达式someBase()
键入someBase()::class
作为不变量KClass
是不正确的,事实上,计算此表达式的实际结果是KClass
为了解决这种可能的不一致性(这将导致破坏类型安全性),所有绑定的类引用都是协变的:someBase()::class
是KClass
,这意味着在运行时someBase()
可能是Base
的子类型,因此这可能是Base
的子类型的类标记
当然,对于未绑定的类引用,情况并非如此:当您使用Base::class
时,您肯定知道它是Base
的类标记,而不是它的某些子类型,因此它是不变的KClass
我明白您的意思。但我的观点是,如果我使用someBase().javaClass.kotlin,我在运行时仍然有正确的子类型(派生),但在编译时也没有变化,因为这些扩展函数使用泛型。在我看来,可以改进::类语法,使其像javaClass.kotlin一样工作,而javaClass.kotlin在习惯上也不那么直接。在我看来,他们做了同样的事情,返回了一个kotlin类,但后者做得更好,同时更详细。反过来说:.javaClass
是较早引入的,它没有变化被认为是一个错误的设计.javaClass
甚至被弃用,但在1.1最终版本中,弃用被删除。有趣的是。然而,如果我必须调用通过反射获取的属性,我需要一个接收器,协变类型不允许我使用绑定类型。在.javaClass
不可用的情况下,您将如何做到这一点?我相信,您必须进行未经检查的强制转换:entry::class as KClass
(或者强制转换从中获得的反射函数/属性)。这看起来和它本身一样不安全,因为假设一个KClass
可以给你一个Base
没有的成员,在Base
的实例上调用该成员函数/属性会导致IllegalArgumentException
。事实上.javaClass
所做的是进行未经检查的强制转换。在实践中,它似乎并不那么不安全,因为当使用反射方法或属性(如memberProperties
)时,我得到的是实例所拥有的,因此我觉得使用同一个接收方实例来获取其自己的属性值是安全的。这就是为什么我会通过设计允许对绑定类引用类型进行强制转换,因为它来自一个在运行时是确定类型的实例。
open class Base
class Derived : Base()
fun someBase(): Base = Derived()
val kClass = someBase()::class