2017-07-18 203 views
2

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

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

+0

这不是物化的问题。你甚至不能'列举''。 –

回答

0

显然我没有制定我的问题得到了我想要的表格的答案。这里的大多数答案都是“因为你不能用Java来做”的一些变化。那么,你不能在Java中使用x instanceof T,但是你可以在Kotlin中使用x is T。我正在寻找潜在的实用障碍,而不是Java规则。毕竟,规则是被打破的。

从我就在这里第一个答案评论,再形成的问题是:如果objectref is T可以做一些机制X为什么不能objectref is SomeClass<T>的是由相同的工作机制在科特林工作?

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

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

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

1

由于Java在编译时将通用类型参数T擦除为Object /上限类型,因此在Kotlin中无法执行此操作。

第一种方法能够工作是因为value is T内联到调用点函数与具体化的类型,例如:

//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 
+0

是的,我得到的实际类型参数将被内联为“是T”为“是字符串”。如果这是可能的,为什么不可能将其内联为“列表”,例如, “列表”?这是我问的问题。 – roobyroo

+0

@roobyroo不,你不能,由于**类型擦除**发生在编译时,所以java不支持** reified **泛型类型。 –

2

阻止你这样做的技术限制是generics type erasure on 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>:type参数不存储在运行时的任何位置。

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

0

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

然而,即使这是可能的,你的示例代码是没有意义的,因为它会检查是否有该名称的类型,而不是检查的的实例列表的,如果与该名称的类型是预期列表类型。

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

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

注意:这个'isListOfA'检查可以咬人:考虑一个'MutableList '的只读视图,它在某个点只包含'String',因此通过'isListOfA (list)'检查, '被添加,而您已经将列表传递给了一些代码,该代码期望所有项目都是'String's,因此失败并带有'ClassCastException'。 – hotkey

相关问题