我最熟悉Java Type Erasure(其所有问题和好处)。我对Kotlin类型系统的扩展可能性有限,但是我对类型的重新化如何在面向擦除的JVM上起作用。什么是类型的重新化,Kotlin如何在JVM上成为可能,这与Java的类型Erasure和Scala的复杂类型系统有何不同?
什么是重新化?
类型重新化是科特林的技巧之一。如果将通用参数声明为 reified
。
由于它是内衬的,因此通用参数可以是具体的class
,而不仅仅是编译时类型的信息。
您可以在Java中做一些不可能的事情:
实例
您现在可以使用instanceof
S(在Kotlin,is
s):
inline fun <reified T> f(a: Any) {
if (a is T) println("Hi!")
}
这显然在Java中是不可能的。
反射
您现在可以从通用参数中获取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()
}
调用其他repified
只有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!")
}
Kotlin中这是不可能的:
inline fun <reified T> f(a: Any) {
}
fun <T> g(a: Any) {
f<T>(a) // error
}
缺点(编辑)
reified
内联函数,则该功能参数将为 java.lang.Object
。您不能使用其他语言调用reified
函数。
喜欢,如果我们在A.kt
中具有REFIFIED功能:
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
。
结论:reified
只能在Kotlin中使用。
关于Scala
真的很难说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-lusing unding函数。
这是一个真正的问题,因为这需要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中手动进行,并且通常是。请注意,这完全是A 完全不同于reified
。
我认为reified
在Scala中没有任何可能的事情,但是Kotlin方法的优势是更自然的语法:例如。在Scala中,您不能只在ClassTag
中写classOf[T]
-使用classOf[File]
的方法。
otoh,scala允许reified
无法做到的事情,例如:
在递归功能中使用
ClassTag
s将它们存储在集合中并使用它们来调用
ClassTag
-稍后使用功能。