为什么Kotlin中的'as'并没有在这种情况下施展

为什么Java在这种情况下打印false

我今天就开始写Kotlin,而我想用泛型来施放参数,但结果是意外的。 而我不知道为什么?

 fun main(args: Array<String>) { var t = TEST<Int, String>() print(t.returns("12") is Int) // print false } @Suppress("UNCHECKED_CAST") class TEST<out T, in R> { fun returns(r: R): T { return r as T } } 

对不起,如果我的英文不好。

由于泛型在JVM上的工作原理,这是因为您忽略@Suppress("UNCHECKED_CAST")给出的警告。

简单地把TEST类编译成以下的java类:

 class TEST { Object returns(Object r) { return r; } } 

因为泛型在运行时没有被通用化,所以这意味着泛型的泛型没有意义。

解决这个问题的一个方法是使用kotlin的被泛化的泛型,一个内联函数和一个匿名类来强制一个真正的强制转换,而不是被简化为Object

 open class TEST<out T, in R> { open fun returns(r: R): T { return r as T } } inline fun <reified T, R> createTest(): TEST<T,R> { return object : TEST<T,R>() { override fun returns(r: R): T { return r as T } } } fun main(args: Array<String>) { var t = createTest<Int, String>() print(t.returns("12") is Int) // Causes classcastexception } 

Java泛型有类型擦除 。 这使得JVM语言很难实现泛化的泛型。 有些语言像Scala那样,有些语言如Kotlin不会 。

这个未经检查的演员是真的没有检查,因此警告。 如果没有泛化泛型,你不能做适当的检查,编译器只会在运行时在泛型和非泛型代码的边界进行类型检查。 因为你的例子没有真正的非泛型部分,所以没有这样的类型检查。 is表达式并不要求左侧是任何特定的类型,所以即使运行时检查也被省略,就像在Java中一样。

考虑下面的例子,返回值存储在一个变量中:

 fun main(args: Array<String>) { var t = TEST<Int, String>() var k = t.returns("12") // runtime error here print(k is Int) } @Suppress("UNCHECKED_CAST") class TEST<out T, in R> { fun returns(r: R): T { return r as T } } ... Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number at MainKt.main(main.kt:3) 

Kotlin在许多地方使用Java-s型检查器。 如果考虑等效的Java代码,则其行为方式相同:

 public class JMain { public static void main(String[] args) { TEST<Integer, String> t = new TEST<>(); System.out.println(t.returns("12") instanceof Integer); // false } static class TEST<T, R> { @SuppressWarnings("unchecked") public T returns(R r) { return (T)r; } } } 

结果是false而不是一个ClassCastException是因为泛型参数类型擦除发生在编译时。

  • 如果类型参数是无界的,则将泛型类型中的所有类型参数替换为它们的边界对象 。 所产生的字节码因此只包含普通的类,接口和方法。
  • 如果需要,插入类型会强制转换以保持类型安全。

所以上面的代码在你的问题返回类型的returns方法是Object而不是Integer 。 并且即使没有额外的转换,也可以将String分配给Object 。 所以你检查一个String是否是一个Int将永远是false

另一种情况是声明一个具有有界参数类型的类 ,这将抛出一个ClassCastException 。 例如:

 @Suppress("UNCHECKED_CAST") class TEST<out T : Int, in R : String> { fun returns(r: R): T { return r as T } } fun main(args: Array<String>) { var t = TEST<Int, String>() print(t.returns("12") is Int) // ^--- throws ClassCastException } 

类型擦除后的有界参数类型的泛型类如下所示:

 public final class TEST{ public int returns(String value){ return ((Integer) value); } }