Kotlin实体类型参数不能作为函数体中的类型参数

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

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> } 

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

JVM上防止你做的是泛型擦除的技术限制。 基本上,在运行时,通用类型List<T>的对象变成了一个与对象一起工作的List :它只在编译时才会检查类型安全性以进行赋值和函数调用。 实际的类型参数T只在编译期间存在,然后被擦除。 它不能在运行时恢复(至少现在是:有一个Project Valhalla可能会为JVM引入运行时实现泛型)。

在一个非内联的Kotlin函数中(和一个非特定类型的参数),你甚至不能做第一种检查, value is T ,因为普通的类型参数也会被擦除。

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

但即使使用了具体的类型参数,也不能反思泛型:你可以检查something is List<*>something is List<String>但不是something is List<String> :类型参数不存储在运行时的任何地方。

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

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

第一种方法可以工作,因为value is T被内联到具体化类型的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类型。

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

 inline fun <reified T> isListOfA(instance: Any) = instance is List<*> && instance.all { it is 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对象。

较长的答案:首先我们必须理解机制X ,它是is 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>是要工作的,那么其他一些机制Y必须在Kotlin中找到并实现。 这种机制可能存在也可能不存在。

我知道有些人可能会说这和其他一些答案是一样的。 然而,无论好坏,我的学习风格都是要理解事物在金属上的运作方式,然后将其与抽象模型进行综合。 在这种情况下,Java泛型删除的概念是抽象模型(或其一部分)。 真的,“删除”对我来说会觉得很卑鄙,除非我至少明白在实施过程中实现的一种方式。