对Generics参数类型的错误转换不会在Java

时间:2016-05-13 10:36:26

标签: java generics casting

所以,我有一个相当深奥的问题。我正在尝试创建一个有点通用但类型化的属性收集系统。它依赖于似乎错误的核心假设。该代码说明了这个问题:

import java.lang.Integer;

public class Test {
    private static Object mObj = new String("This should print");

    public static void main(String[] args ) {
    String s = Test.<String>get();
    System.out.println(s);

        try {
        // actual ClassCastException reported HERE
        int i = Test.<Integer>get();
    } catch ( ClassCastException e ) {
        System.out.println("Why isn't the exception caught earlier?");
        }

        int i2 = getInt();
    }

    public static <T> T get() {
    T thing = null;
    try {
        // Expected ClassCastException here
        thing = (T)mObj;
    } catch ( ClassCastException e ) {
        System.out.println("This will *not* be printed");
    }
    return thing;
    }

    // This added in the edit
    public static Integer getInt() {
    return (Integer)mObj;
    }
}

编译并运行输出后

This should print
Why isn't the exception caught earlier?

在静态方法“get”中,我试图转换为泛型参数类型T.底层成员(mObj)是String类型。在第一次调用中,Generic参数是兼容类型,因此app会相应地打印字符串。

在第二次调用中,Generic参数的类型为Integer。因此,get方法中的强制转换应该失败。我希望它会抛出一个ClassCastException(打印语句“这将*不打印”。)但这不是发生的事情。

相反,当尝试将返回值分配给变量“i”时,get方法返回后抛出 <\ n> <\ n> 。这是问题:

这种延迟投射错误的解释是什么?

**编辑** 为了有趣和完整,我添加了一个非泛型的getInt方法来说明我希望得到的响应。令人惊讶的是当编译器知道类型时会发生什么。

5 个答案:

答案 0 :(得分:4)

这是因为get方法中的强制转换(应生成编译器警告)是未经检查的强制转换。当你调用T时,编译器不知道thing = (T) mObj;是什么,因此无法知道强制转换是否应该编译。

编译后,由于类型擦除,生成的字节码调用方法get,返回ObjectT被删除并替换为Object)和演员简单地翻译成:

Object thing = null;
try {
    thing = mObj;

在结果从get方法返回后正在进行检查,即int i = Test.<Integer> get();相当于:

Object o = Test.get();
int i = ((Integer) o).intValue();

答案 1 :(得分:0)

这可能是因为在运行时jvm不知道T是什么,并且它将它视为Object,所以当通用类型已知时,对Integer的实际强制转换正在进行,所以这里: int i = Test.<Integer>get();

答案 2 :(得分:0)

java中的泛型是用擦除实现的。这意味着在运行时,java没有使用泛型类型的含义,而是使用Object。

在运行时&#34;事情&#34;是一个对象,并且为对象分配任何内容都不会引发任何错误。

答案 3 :(得分:0)

int i = Test.<Integer>get();

坚持认为结果是调用者类型。因此行

thing = (T)mObj;

强制整数obj为整数:

$char = ",test,test2,test3,";
echo trim($char,',');

但是mObj是String的一个实例。

答案 4 :(得分:0)

  

为了实现泛型,Java编译器将类型擦除应用于:

     

将泛型类型中的所有类型参数替换为其边界或   对象,如果类型参数是无界的。

REF:http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

因此,代码

public static <T> T get() {
T thing = null;
try {
    // Expected ClassCastException here
    thing = (T)mObj;
} catch ( ClassCastException e ) {
    System.out.println("This will *not* be printed");
}
return thing;
}

在运行时应擦除为

public static Object get() {
Object thing = null;
try {
    // Expected ClassCastException here
    thing = (Object)mObj;
} catch ( ClassCastException e ) {
    System.out.println("This will *not* be printed");
}
return thing;
}

因为T无界限。理解这一点,然后很容易解释输出。