![](/img/trans.png)
[英]Why can't type parameter in Kotlin have any other bounds if it's bounded by another type parameter?
[英]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
对象。
更长的答案:首先我们必须了解机制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>
要工作,则必须在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>
实例,无论T
和V
是否已实现或硬编码。
但是,即使可能,您的示例代码也没有意义,因为它检查具有该名称的类型是否是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.