当实例被强制转换为泛型类型时,编译器将报告警告



我看不懂下面的代码。编译器报告一个警告。据我所知,泛型类型被擦除。但我不知道为什么代码会产生警告。有人能帮我吗?

  public class Generic<T>{
            public void method(){
                    Object obj = new Object();
                    T t = (T)obj; // compile warning
            }
  }

执行命令"javac Generic.java -Xlint:unchecked"后,在

下面打印警告信息。
Generic.java:4: warning: [unchecked] unchecked cast
        T t = (T) obj; // compile warning 
                  ^
  required: T
  found:    Object
  where T is a type-variable:
    T extends Object declared in class Generic

编译器发出警告,因为它可能是危险的。特别是,它可能导致难以跟踪的classcastexception。让我们看看为什么。

下面是稍微修改过的代码:

public class Generic<T> {
    public T method() {
        Object obj = new Object();
        T t = (T) obj; // warning
        return t;
    }
}

由于类型擦除,JVM不能在运行时检查obj是否是T的实例;强制转换在运行时实际上是无操作的。现在考虑这个方法,它接受一个Generic<T>并将它的对象添加到一个列表中:

public static <T> void addToList(Generic<T> generic, List<T> list) {
    T t = generic.method();
    list.add(t);
}

最后,让我们使用addToList方法:

List<Integer> integers = new ArrayList<>();
Generic<Integer> genericInt = new Generic<>();
addToList(genericInt, integers);
Integer theInt = integers.get(0); // ClassCastException!

由于该类型擦除,addToList不能进行任何类型检查(方法中的T只是编译为T可能是的最特定类型,在本例中是Object,因为T未绑定)。这意味着它运行成功,但它将new Object()("转换"到T)放在应该是List<Integer>的地方。在尝试取出Integer之前,这一切都是有效的,此时编译器被迫尝试将Object转换为Integer。最后一行编译成如下内容:

Integer theInt = (Integer) (integers.get(0));

这会抛出一个ClassCastException,因为第一个元素被实例化为new Object()(因此不是Integer)。

在这个玩具应用程序中,追踪实际问题相对简单。但是考虑一个更大的应用程序,其中各种东西将元素放入List。现在您得到了一个ClassCastException,查找将非Integer对象放在列表中的错误代码可能会很棘手。

所以,关于不安全强制转换的警告是真实的——编译器试图告诉您,强制转换不能在运行时检查,因此它是不安全的,因为它可能导致难以调试的错误。

两年前我看到自己提出这个问题,但现在我已经明白了。首先像这样修改泛型类的定义

public class Generic<T> {
    public T method() {
        Object obj = new Object();
        T t = (T) obj;
        return t;
    }
}

然后运行下面的代码。

Generic<String> generic = new Generic<String>();
System.out.println("" + generic.method());

将发生崩溃。crash的信息为

java.lang.ClassCastException:java.lang.Object不能强制转换为java.lang.String

最新更新