为什么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
}
}
对不起,如果我的英语不好。
答案 0 :(得分:3)
由于泛型如何在JVM上工作,这是因为您忽略了@Suppress("UNCHECKED_CAST")
给出的警告。
只需将TEST
类编译为以下java类:
class TEST {
Object returns(Object r) {
return r;
}
}
因为泛型在运行时没有被统一,这意味着带有泛型的强制转换是没有意义的。
解决这个问题的一种方法是使用kotlin的reified
泛型,内联函数和匿名类来强制进行实际演员,而不是将其简化为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
}
答案 1 :(得分:1)
Java泛型有type erasure。这使得JVM语言难以实现具体化的泛型。 Some languages like Scala do, some languages like Kotlin do not
这种未经检查的演员表实际上是未经检查的,因此警告。如果没有具体的泛型,则无法进行正确的检查,编译器只会在运行时在泛型和非泛型代码的边界处进行类型检查。因为您的示例实际上没有非泛型部分,所以不会执行此类类型的检查。 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;
}
}
}
答案 2 :(得分:1)
结果是false
而不是ClassCastException
,因为通用参数Type Erasure发生在编译时。
- 如果类型参数不受限制,则将通用类型中的所有类型参数替换为其边界或对象。因此,生成的字节码只包含普通的类,接口和方法。
- 如有必要,插入类型强制转换,以保护类型安全。
因此,您问题中上面的代码returns
方法的返回类型为Object
而不是Integer
。即使没有额外的投射,String
也可以分配给Object
。因此,您检查String
Int
是否始终为false
。
另一种情况是使用bounded parameter types声明一个类,它将抛出一个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
}
在使用bounded parameter types的通用类的类型擦除后如下所示:
public final class TEST{
public int returns(String value){
return ((Integer) value);
}
}