我最熟悉Java类型擦除(包括所有问题和好处)。我对Kotlin类型系统的扩展可能性有一些有限的了解,但是我还没有清楚地了解类型验证如何在面向擦除的JVM上工作。什么是类型修改,Kotlin如何在JVM上实现,以及它与Java的类型擦除和Scala的复杂类型系统有何不同?
类型具体化是Kotlin的技巧之一。如果将泛型参数声明为reified
,则仅在内联泛型函数中发生。
由于它是内联的,泛型参数可以是具体的class
,而不仅仅是编译时类型信息。
你可以在Java中做一些不可能的事情:
你现在可以使用instanceof
s(在Kotlin,is
s):
inline fun <reified T> f(a: Any) {
if (a is T) println("Hi!")
}
这在Java中显然是不可能的。
您现在可以从generic参数获取java java.lang.Class<T>
实例。
inline fun <reified T> f(a: Any) {
println("Hey! my class is ${T::class.java}!")
if (a.javaClass == T::class.java) println("Hi!")
}
另外,KClass
也是:
inline fun <reified T> f(a: Any) {
println("KClass: ${T::class}")
}
您可以使用空构造函数创建实例:
inline fun <reified T> f(a: Any) {
val o: T = T::class.java.newInstance()
}
只有reified
泛型参数可以传递给其他reified
函数。
inline fun <reified T> f(a: Any) {
g<T>(a)
}
inline fun <reified T> g(a: Any) {
if (a is T) println("Bingo!")
}
这在科特林是不可能的:
inline fun <reified T> f(a: Any) {
}
fun <T> g(a: Any) {
f<T>(a) // error
}
如果您使用其他语言在Kotlin中调用reified
内联函数,则函数参数将为java.lang.Object
。
您不能使用其他语言来调用reified
函数。
就像,如果我们在A.kt
中有一个具体的功能:
inline fun <reified T> f(a: T) = println(T::class.java)
并使用反射获取它(它将被编译为私有):
Method method = AKt.class.getDeclaredMethod("f", Object.class);
此代码将成功运行,没有例外。 但你不能调用它(我没有仔细阅读生成的字节码,对不起),因为它的实施:
private static final void f(Object a) {
Intrinsics.reifiedOperationMarker(4, "T"); // I didn't see
// the implementation of this line, so I thought it's
// possible to call it in other languages
Class var2 = Object.class;
System.out.println(var2);
}
看看评论。看看reifiedOperationMarker
的定义:
public static void reifiedOperationMarker(int id, String typeParameterIdentifier) {
throwUndefinedForReified();
}
它会抛出一个UnsupportedOperationException
。
结论:reifeid
只能用于Kotlin。
很难说Kotlin或Scala是否更好,因为Scala有更多的方法可以在运行时获取类型信息。
Alexey Romanov说Scala可以,但Kotlin不能:
在递归函数中使用ClassTags
我认为这可以通过使用函数内部的函数来解决:
inline fun <reified T> g(a: Any): Int {
var recur: ((Any) -> T)? = null
recur = { recur!!.invoke(it) as T } // use T is possible here
return recur(a)
}
请注意,这只是语法上正确的示例。 当然,它是无限循环和不必要的演员。
他还说:
将它们存储在集合中并使用它们稍后调用ClassTag-using函数。
这是一个真正的问题,因为这需要noinline
lambdas,而Kotlin的reified
是基于内联的。
当Kotlin内联泛型函数时,它自然会用调用它的类型替换类型参数。例如。与inline fun <T> foo(x: T) = ...
foo(File("."))
变
val x = File(".")
// body of foo with File used everywhere T was
reified
所做的只是允许在foo
体中使用操作,这种操作只有在这种替换后才有意义,但对于非reified
类型参数(例如T::class
)是非法的。
相关的Scala功能是ClassTag
/ TypeTag
,而不是“复杂类型系统”。实际上,它可以自动将Class<T>
(或TypeToken<T>
)作为参数传递,这可以通过Java手动完成。请注意,这是一种与reified
完全不同的方法。
我不认为reified
做的任何事情在Scala中是不可能的,但Kotlin方法的优点是更自然的语法:例如在Scala你不能像classOf[T]
那样用ClassTag
使用方法写classOf[File]
。
OTOH,Scala允许用reified
无法实现的东西,例如:
ClassTag
sClassTag
-using函数。