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