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(x: T) = ...

 foo(File(".")) 

 val x = File(".") // body of foo with File used everywhere T was 

什么是reified ,只是允许在foo使用操作,这个操作在这个替换之后才有意义但是对于非reifiedtypes参数,例如T::class ,则是非法的。

相关的Scalafunction是ClassTag / TypeTag ,而不是“复杂的types系统”。 实际上,它会自动将Class (或TypeToken )作为parameter passing,这可以通过Java手动完成, 请注意,这是一个完全不同于reified方法。

我不认为在Scala中有什么reified的东西是不可能的,但是Kotlin方法的优点是更自然的语法:例如在Scala中,你不能只用ClassTag方法写classOf[T]像你会classOf[File]

OTOH,Scala允许通用化的东西,例如:

  1. 在递归函数中使用ClassTag

  2. 将它们存储在集合中,然后使用它们来调用ClassTag