Kotlin的类型具体化在Java或Scala中是不可能实现的?

Nat*_*han 8 java generics types scala kotlin

我最熟悉Java类型擦除(包括所有问题和好处).我对Kotlin类型系统的扩展可能性有一些有限的了解,但是我并没有清楚地了解类型验证如何在面向擦除的JVM上工作.什么是类型修改,Kotlin如何在JVM上实现,以及它与Java的类型擦除和Scala的复杂类型系统有何不同?

ice*_*000 6

什么是具体化?

类型具体化是Kotlin的技巧之一.如果将泛型参数声明为,则仅在内联泛型函数中发生reified.

由于它是内联的,因此泛型参数可以是具体的class,而不仅仅是编译时类型信息.
你可以在Java中做一些不可能的事情:

的instanceof

你现在可以使用instanceofs(在Kotlin中,iss):

inline fun <reified T> f(a: Any) {
    if (a is T) println("Hi!")
}
Run Code Online (Sandbox Code Playgroud)

这在Java中显然是不可能的.

反射

您现在可以java.lang.Class<T>从generic参数获取java 实例.

inline fun <reified T> f(a: Any) {
    println("Hey! my class is ${T::class.java}!")
    if (a.javaClass == T::class.java) println("Hi!")
}
Run Code Online (Sandbox Code Playgroud)

另外,KClass还有:

inline fun <reified T> f(a: Any) {
    println("KClass: ${T::class}")
}
Run Code Online (Sandbox Code Playgroud)

您可以使用空构造函数创建实例:

inline fun <reified T> f(a: Any) {
    val o: T = T::class.java.newInstance()
}
Run Code Online (Sandbox Code Playgroud)

打电话给其他人

只有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!")
}
Run Code Online (Sandbox Code Playgroud)

这在科特林是不可能的:

inline fun <reified T> f(a: Any) {
}

fun <T> g(a: Any) {
    f<T>(a) // error
}
Run Code Online (Sandbox Code Playgroud)

缺点(编辑)

如果您使用其他语言reified在Kotlin中调用内联函数,则函数参数将为java.lang.Object.

您不能使用其他语言来调用reified函数.

就像,如果我们有一个具体的功能A.kt:

inline fun <reified T> f(a: T) = println(T::class.java)
Run Code Online (Sandbox Code Playgroud)

并使用反射获取它(它将被编译为私有):

Method method = AKt.class.getDeclaredMethod("f", Object.class);
Run Code Online (Sandbox Code Playgroud)

此代码将成功运行,没有例外.
但你不能调用它(我没有仔细阅读生成的字节码,对不起),因为它的实施:

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);
}
Run Code Online (Sandbox Code Playgroud)

看看评论.看看定义reifiedOperationMarker:

public static void reifiedOperationMarker(int id, String typeParameterIdentifier) {
    throwUndefinedForReified();
}
Run Code Online (Sandbox Code Playgroud)

它会抛出一个UnsupportedOperationException.

结论:reified只能用于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)
}
Run Code Online (Sandbox Code Playgroud)

请注意,这只是一个语法上正确的例子.
当然,它是无限循环和不必要的演员.

他还说:

将它们存储在集合中并使用它们稍后调用ClassTag-using函数.

这是一个真正的问题,因为这需要noinlinelambdas,而Kotlin reified则基于内联.