使绑定的类引用返回协变类型的目的是什么? [英] What is the purpose of having bound class reference return a covariant type?

查看:103
本文介绍了使绑定的类引用返回协变类型的目的是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在思考,我遇到了这个问题.通过::class语法使用绑定类引用时,我得到了协变KClass类型:

I'm playing with reflection and I came out with this problem. When using bound class reference via the ::class syntax, I get a covariant KClass type:

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

我可以从文档中学到,如果它是T子类型的实例,则将返回对象的确切类型,因此可以使用方差修饰符. 但是,这会阻止检索在T类中声明的属性并获取它们的值(这是我正在尝试做的事情)

As I could learn from the docs, this will return the exact type of the object, in case it is instance of a subtype of T, hence the variance modifier. However this prevents retrieving properties declared in the T class and getting their value (which is what I'm trying to do)

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扩展函数来获取不变类型:

I found that a solution is using javaClass.kotlin extension function on the object reference, to get instead the invariant type:

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

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

This way, I get both the exact type at runtime and the possibility to consume the type.

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

Interestingly, if I use a supertype instead of a generic, with the latter method I still get access to the correct type, without the need of variance:

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应该按设计返回不变类型.

If I got it correct, ::class is equal to calling the java getClass, which returns a variant type with a wildcard, while javaClass is a getClass with a cast to the specific type. Still, I don't get why would I ever need a covariant KClass, when it limits me to only produce the type, given that there are other ways to access the exact class at runtime and use it freely, and I wonder if the more immediate ::class should return an invariant type by design.

推荐答案

绑定的::class引用中协方差的原因是,表达式被评估为的对象的实际运行时类型可能与声明的或推断的类型不同.表达式.

The reason for covariance in bound ::class references is, the actual runtime type of an object the expression is evaluated to might differ from the declared or inferred type of the expression.

示例:

open class Base
class Derived : Base()

fun someBase(): Base = Derived()

val kClass = someBase()::class

表达式someBase()键入为Base,但是在运行时它是一个Derived对象,对其进行求值.

The expression someBase() is typed as Base, but at runtime it's a Derived object that it gets evaluated to.

someBase()::class键入为不变式KClass<Base>是完全不正确的,实际上,求该表达式的实际结果是KClass<Derived>.

Typing someBase()::class as invariant KClass<Base> is simply incorrect, in fact, the actuall result of evaluating this expression is KClass<Derived>.

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

To solve this possible inconsistency (that would lead to broken type-safety), all bound class references are covariant: someBase()::class is KClass<out Base>, meaning that at runtime someBase() might be a subtype of Base, and therefore this might be a class token of a subtype of Base.

当然,对于未绑定的类引用,情况并非如此:当您使用Base::class时,您肯定知道它是Base的类标记,而不是其某些子类型,因此它是不变的.

This is, of course, not the case with unbound class references: when you take Base::class, you know for sure that it's the class token of Base and not of some of its subtypes, so it's invariant KClass<Base>.

这篇关于使绑定的类引用返回协变类型的目的是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆