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>
}
这是由于技术限制吗?如果是这样,那是什么限制?
答案 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>
实例,无论T
和V
是否已实现或硬编码。
但是,即使可能,您的示例代码也没有意义,因为它检查具有该名称的类型是否为List的实例,而不是检查具有该名称的类型是否为预期的列表类型。
如果你有一个实例的对象,并且想要检查它是否只包含预期类型的项目,你仍然可以这样写:
inline fun <reified T> isListOfA(instance: Any)
= instance is List<*> && instance.all { it is T }