Java泛型方法返回不同的类型 - 没有异常

时间:2013-08-09 09:32:34

标签: java generics methods

我发现了一些奇怪的代码,我说“这个从未调用过,因为它会抛出一个类强制转换异常”。那么代码就会被调用并且正在运行。

任何人都可以解释一下:为什么会这样?

方法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;
        }
    }
}

非常感谢您的解释。

5 个答案:

答案 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一样,你不能这样做。抛出异常。

总结一下,您已经创建了一个代码来控制类型保证,但您已经实现了它的正确用法。这就是它的工作原因。