我发现了一些奇怪的代码,我说“这个从未调用过,因为它会抛出一个类强制转换异常”。那么代码就会被调用并且正在运行。
任何人都可以解释一下:为什么会这样??
方法getZipList()被定义为返回一个字符串列表,但内部逻辑正在返回一个包含不同对象的List。同样在main方法中,字符串列表期望为“列表”,但列表包含不同的内容。
public class GenericMethodList{
public static void main(String [] args)
{
GenericMethodList o = new GenericMethodList();
List<String> list = o.getZipList(true);
Iterator<?> iter = list.iterator();
while (iter.hasNext()){
ZipCode zipplace = (ZipCode) iter.next();
System.out.println(zipplace.value);
}
}
public List<String> getZipList(boolean someParameter){
//why is this not throwing an exception at runtime?
List list;
if(someParameter){
list = getZipCodes();//List<ZipCode>
} else {
list = getZipStreets();//List<ZipStreet>
}
return list;
}
private List<ZipCode> getZipCodes(){
List<ZipCode> list = new ArrayList<ZipCode>();
list.add(new ZipCode("code1"));
list.add(new ZipCode("code1"));
return list;
}
private List<ZipStreet> getZipStreets(){
List<ZipStreet> list = new ArrayList<ZipStreet>();
list.add(new ZipStreet("street1"));
list.add(new ZipStreet("street2"));
return list;
}
public class ZipCode{
public String value;
public ZipCode(String value){
this.value = value;
}
}
public class ZipStreet {
public String value;
public ZipStreet(String value){
this.value = value;
}
}
}
非常感谢您的解释。
答案 0 :(得分:4)
您必须为return list
行获取“未经检查的强制转换”编译器警告,因为您将原始类型作为参数化类型返回。在这个位置,从原始类型(对于元素的类型没有信息)执行“未检查的强制转换”到为该方法声明的参数化类型。 type参数可以是任意类型 - 编译器根本不知道允许什么以及要防止什么。
这种转换在运行时无法执行的意义上是未选中的:在运行时,list
实例的类型参数无论如何都会被删除,因此JVM不知道你在那里做了坏事。 / p>
答案 1 :(得分:0)
因为您使用的是原始类型“List”(即未指定类型参数的泛型类型)。 原始类型用于遗留目的,但应该在新代码中避免使用,因为它失去了类型安全性,如您所见:
http://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html
答案 2 :(得分:0)
你应该收到警告。由于类型擦除(.class
文件实际上没有为反向兼容性列出参数化类型),List<E>
的运行时类型只是List
,并且没有看到JVM任何差异。当有人试图从String
中取出List
并获得ZipCode
时,错误会出现在远处。
答案 3 :(得分:0)
你必须在编译时接受以下交战。
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
由于泛型基于type erasure的实现。所以在运行时它没有关于Generic类型的信息(通常称为Non-Reifiable Types)
答案 4 :(得分:0)
问题是您将解决方案RawType与通用组合在一起。您可以在方法getZipList(boolean)
中执行此操作。由于List
没有类型,你可以制动类型保证。
你欺骗编译器的下一个地方是声明Iterator<?>
的方式,因为你没有声明我将存储对象的泛型参数。因此,下次您避免使用类型保证时,您可以利用它来查看转换为预期类型的位置。这就是它的工作原因。通常情况下,编译是由编译器执行的,但是您可以通过自己正确地实现它。
如果您的代码看起来像这样
Iterator<String> iter = list.iterator();
while (iter.hasNext()){
ZipCode zipplace = (ZipCode) iter.next();// Compilation error
System.out.println(zipplace.value);
}
如果您的主要方法如下所示:
while(String s : list) {
System.out.wirteln(s);
}
将抛出ClassCastException。因为代码“看起来像”
Iterator<Object> iter = list.iterator();
while (iter.hasNext()){
ZipCode zipplace = (String) iter.next();
System.out.println(zipplace.value);
}
与Java一样,你不能这样做。抛出异常。
总结一下,您已经创建了一个代码来控制类型保证,但您已经实现了它的正确用法。这就是它的工作原因。