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

时间:2017-07-18 17:57:16

标签: types parameters kotlin inline

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

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

然而,当我尝试使用&#34; T&#34;作为一个类型参数而不是独立的我得到一个消息,它是一个擦除类型。以下代码仅用于说明目的

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

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

4 个答案:

答案 0 :(得分:3)

阻止您这样做的技术限制是generics type erasure on 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处理这种奇怪的情况:只有该类型的非泛型部分可以在运行时实际检查,所以它就是。

答案 1 :(得分:3)

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

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

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

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

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

答案 2 :(得分:1)

由于Java在编译时将泛型类型参数T擦除为Object /上限类型,因此无法在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

答案 3 :(得分:0)

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

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

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

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