繁体   English   中英

Kotlin reified类型参数不能用作函数体中的类型参数

[英]Kotlin reified type parameter can't be used as type parameter in body of function

Kotlin中的reified类型参数可防止类型参数擦除,并允许在运行时知道type参数。 这允许以下代码按预期编译和运行:

inline fun <reified T> isA(value: Any) = value is T

但是,当我尝试使用“T”作为类型参数而不是独立时,我得到一条消息,它是一个擦除类型。 以下代码证明了这一点,仅用于说明目的

inline fun <reified T> isListOfA(name: String): Boolean {
    val candidate = Class.forName(name)
    return candidate is List<T>
}

这是由于技术限制吗? 如果是这样,那个限制是什么?

显然我没有恰当地提出我的问题来得到我想要的形式的答案。 这里的大多数答案都是“因为你不能用Java做的”的一些变体。 好吧,你不能在Java中使用x instanceof T ,但你可以在Kotlin中做x is T 我正在寻找潜在的实际障碍,而不是Java规则。 毕竟,规则被打破了。

从我对这里第一个答案的评论,重新提出的问题是:如果objectref is T可以通过某种机制X在Kotlin中工作,为什么不能使用objectref is SomeClass<T>通过相同的机制工作?

tl; dr回答:因为在运行时没有SomeClass<T> Class对象。

更长的答案:首先我们必须了解机制Xis T生成字节码指令的instanceof 该指令采用objectref和某些类C的名称N ,其中N由编译器根据上下文确定。 在运行时,从N派生的类C将用于评估objectref is T表达式。 为了进行此评估,必须实例化C的类对象。 因此,对于objectref is SomeClass<T>使用相同的机制objectref is SomeClass<T>那么N将是SomeClass<T> 由于类型擦除, SomeClass<T>不会有类对象,因此无法生成所需的instanceof指令,从而应用相同的机制。 此外, instanceof指令不能采用SomeClass<T>形式的名称。 因此,如果objectref is SomeClass<T>要工作,则必须在Kotlin中找到并实现其他一些机制Y 这种机制可能存在也可能不存在。

我知道有些人可能会说这与其他一些答案是一回事。 然而,无论好坏,我的学习方式是了解事物如何在金属上起作用,然后将其与抽象模型相结合。 在这种情况下,Java Generics擦除概念是抽象模型(或其一部分)。 真的,“擦除”对我来说感觉很软弱,除非我至少了解它在工作实现中实现的一种方式。

阻止您这样做的技术限制是JVM上的泛型类型擦除 基本上,在运行时,泛型类型List<T>对象只是一个与对象一起工作的List :它只在编译时才检查类型安全性的赋值和函数调用。 实际类型参数T仅在编译期间存在,然后被擦除。 它无法在运行时恢复(至少目前为止:有一天Project Valhalla可能会为JVM引入运行时验证的泛型)。

在非内联Kotlin函数(以及非reified类型参数)中,您甚至无法进行第一种检查, value is T ,因为普通类型参数也会被删除。

使用reified类型参数,函数体在其调用站点内联,实际(或推断)类型参数替换为T :当您调用isA<String>("abc") ,调用站点将具有字节码instanceof检查String

但是即使使用了reified类型参数,也无法对内部类型进行内省:您可以检查something is List<*>但不是something is List<String> :类型参数不会存储在运行时的任何位置。

另请注意isA<List<String>>(listOf(1, 2, 3))将返回true 这就是Kotlin处理这个奇怪的情况:只有该类型的非泛型部分可以在运行时实际检查,所以它是。

因为Java在编译时将泛型类型参数T擦除为Object / upper-bounded类型,所以在Kotlin中无法做到这一点。

第一种方法可以工作,因为value is T被内联到具有reified类型的call-site函数,例如:

//val is_string = isA<String>(1) // inline into the call-site function as below:


val i:Int = 1
//                   v--- the actual type argument is inlined here
val is_string = 1 is String

参数化类型总是在运行时擦除。 因此,您可以检查值是否是T实例但不是T<V>实例,无论TV是否已实现或硬编码。

但是,即使可能,您的示例代码也没有意义,因为它检查具有该名称的类型是否是List的实例 ,而不是检查具有该名称的类型是否是预期的List类型。

如果你有一个对象的实例 ,并想要检查它是否只包含期望类型的项目,你仍然可以这样写:

inline fun <reified T> isListOfA(instance: Any)
    = instance is List<*> && instance.all { it is T }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM