Java或Scala中Kotlin的types实现是不可能实现的?
我最熟悉的Javatypes擦除(及其所有的问题和好处)。 我对Kotlintypes系统的扩展可能性有一些限制,但是我不清楚types实例是如何在面向擦除的JVM上工作的。 什么是types实现,Kotlin如何在JVM上实现,这与Java的types擦除和Scala的复杂types系统有何不同?
什么是物化?
types化是Kotlin的伎俩之一。 它只发生在内联generics函数中,如果你声明通用参数为reified
。
由于它是内联的,generics参数可以是一个具体的class
,而不仅仅是一个编译时types信息。
你可以在Java中做一些不可能的事情:
的instanceof
你现在可以使用instanceof
(在Kotlin中is
s):
inline fun f(a: Any) { if (a is T) println("Hi!") }
在Java中这显然是不可能的。
reflection
现在可以从generics参数中获取java java.lang.Class
实例。
inline fun f(a: Any) { println("Hey! my class is ${T::class.java}!") if (a.javaClass == T::class.java) println("Hi!") }
另外,还有KClass
:
inline fun f(a: Any) { println("KClass: ${T::class}") }
您可以使用空构造函数创建实例:
inline fun f(a: Any) { val o: T = T::class.java.newInstance() }
打电话给其他人
只有通用参数可以传递给其他通用函数。
inline fun f(a: Any) { g(a) } inline fun g(a: Any) { if (a is T) println("Bingo!") }
在Kotlin这是不可能的:
inline fun f(a: Any) { } fun g(a: Any) { f (a) // error }
缺点(编辑)
如果您使用其他语言来调用Kotlin中的内联函数,函数参数将是java.lang.Object
。
您不能使用其他语言来调用一个指定的函数。
就像,如果我们在A.kt
有一个具体的函数:
inline fun f(a: T) = println(T::class.java)
并使用reflection(它将被编译为私有):
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。
关于Scala
很难说Kotlin或者Scala是否更好,因为Scala在运行时有更多的获取types信息的方法。
阿列克谢·罗曼诺夫说,斯卡拉可以,但科特林不能:
在递归函数中使用ClassTags
我认为这可以通过使用函数内的函数来解决:
inline fun g(a: Any): Int { var recur: ((Any) -> T)? = null recur = { recur!!.invoke(it) as T } // use T is possible here return recur(a) }
请注意,这只是一个在语法上正确的例子。
当然是无限循环和不必要的投射。
他还说:
将它们存储在集合中并稍后使用它们来调用ClassTag使用函数。
这是一个真正的问题,因为这需要noinline
lambda,而noinline
是基于inline。
当Kotlin内联一个generics函数时,它自然会用它所调用的types替换types参数。 例如, inline fun
foo(File("."))
变
val x = File(".") // body of foo with File used everywhere T was
什么是reified
,只是允许在foo
使用操作,这个操作在这个替换之后才有意义,但是对于非reified
types参数,例如T::class
,则是非法的。
相关的Scalafunction是ClassTag
/ TypeTag
,而不是“复杂的types系统”。 实际上,它会自动将Class
(或TypeToken
)作为parameter passing,这可以通过Java手动完成, 请注意,这是一个完全不同于reified
方法。
我不认为在Scala中有什么reified
的东西是不可能的,但是Kotlin方法的优点是更自然的语法:例如在Scala中,你不能只用ClassTag
方法写classOf[T]
像你会classOf[File]
。
OTOH,Scala允许通用化的东西,例如:
-
在递归函数中使用
ClassTag
-
将它们存储在集合中,然后使用它们来调用
ClassTag
。