6

我发现了一些奇怪的代码,我说“这永远不会被调用,因为它会抛出一个类强制异常”。那么代码被调用并且它的工作。

任何人都可以解释我:为什么这有效

方法 getZipList() 被定义为返回一个字符串列表,但内部逻辑返回一个包含不同对象的列表。同样在 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;
        }
    }
}

非常感谢您的解释。

4

5 回答 5

4

您必须收到该return list行的“未经检查的强制转换”编译器警告,因为您将原始类型作为参数化类型返回。在这个位置,从原始类型(根本没有关于元素类型的信息)执行“未经检查的强制转换”到为方法声明的参数化类型。类型参数可以是任意类型——编译器根本不知道允许什么和阻止什么。

从某种意义上说,这种强制转换是未经检查的,因为它不能在运行时执行:在运行时,list实例的类型参数无论如何都会被删除,所以 JVM 不知道你在那里做了坏事。

于 2013-08-09T09:36:47.687 回答
0

因为您使用的是原始类型“列表”(即没有指定类型参数的泛型类型)。原始类型用于遗留目的,但在新代码中应避免使用,因为它失去了类型安全性,如您所见:

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

于 2013-08-09T09:37:51.970 回答
0

你应该收到警告。由于类型擦除(该.class文件实际上并没有列出参数化类型以实现向后兼容性), a 的运行时类型List<E>只是List,并且 JVM 没有发现任何差异。当有人试图从中拉出 a 并取而代之时,错误将出现在遥远StringList位置ZipCode

于 2013-08-09T09:38:37.000 回答
0

您必须在编译时低于交战。

 Note: Some input files use unchecked or unsafe operations.
 Note: Recompile with -Xlint:unchecked for details.

由于泛型是基于类型擦除的实现。所以在运行时它没有关于通用类型的信息(一般称为Non-Reifiable Types

于 2013-08-09T09:42:54.407 回答
0

问题是您在解决方案中将 RawType 与泛型结合在一起。您在方法中执行此操作getZipList(boolean)。由于List没有类型,因此您会破坏类型保证。下一个欺骗编译器的地方是你声明的方式Iterator<?>,因为你没有声明泛型参数 i will store object. 因此,下次您避免类型保证时,您正在利用查找您转换为预期类型的​​位置。这就是为什么它的工作。通常转换是由编译器执行的,但你已经以正确的方式自己实现了它。

如果您的代码看起来像这样

 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 一样,您不能以这种方式进行转换。抛出异常。

总而言之,您已经创建了一个阻止类型保证的代码,但您已经实现了它的正确用法。这就是为什么它的工作。

于 2013-08-09T09:59:14.523 回答