迭代参数化List(在原始List类型赋值之后)

时间:2012-09-09 15:01:15

标签: java generics classcastexception foreach

语言:Java
编译器版本:1.6

在下面的代码中,我尝试执行以下操作:

  1. 创建List<String>
  2. 添加String
  3. List<String>分配给原始List
  4. 创建List<Integer>
  5. 将原始List分配给List<Integer>
  6. 添加Integer
  7. 使用get() @index 1&amp; 2并打印出来。
  8. 所有语句都在编译(带警告)并运行正常。

    但如果我尝试使用List<Integer>循环遍历for,我会得到ClassCastException。我只是想知道为什么它允许我使用list.get()方法但不允许我迭代它?

    输出: (如果我使用未评论的for循环运行)abcd 200

    Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot  be cast to java.lang.Integer
            at genericsamples.CheckRawTypeAdd.main(CheckRawTypeAdd.java:26)
    

    这是我的代码

    import java.util.*;
    import java.io.*;
    class CheckRawTypeAdd
    {
        public static void main(String[] sr)
        {   
            List<String> list_str = new ArrayList<String>();
            list_str.add("abcd");
            List<Integer> list_int = new ArrayList<Integer>();  
            List list_raw; 
            list_raw=list_str;
            list_int=list_raw;
            list_int.add(200);
            Object o1 = list_int.get(0);
            Object o2 = list_int.get(1);        
            System.out.println(o1);
            System.out.println(o2);
            //for(Object o : list_int)
            //{
            //  System.out.println("o value is"+o);
            //}
        }
    }
    

2 个答案:

答案 0 :(得分:5)

我认为这是javac中的编译器错误。插入已检查的演员表。我们可以使用javap -c CheckRawTypeAdd来反汇编这个类(强制转换为101;注意我在编译之前已经取出了一些不需要的代码行,所以代码点会有所不同):

  77: invokeinterface #10,  1           // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
  82: astore        6
  84: aload         6
  86: invokeinterface #11,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
  91: ifeq          109
  94: aload         6
  96: invokeinterface #12,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
 101: checkcast     #13                 // class java/lang/Integer

但是,Java Language Spec (14.14.2)表示此演员应该是Object,而不是Integer。首先是通过语法定义术语:

EnhancedForStatement:
    for ( FormalParameter : Expression ) Statement

FormalParameter:
    VariableModifiersopt Type VariableDeclaratorId

VariableDeclaratorId:
    Identifier
    VariableDeclaratorId []

因此,在我们的案例中,TypeObject。然后它继续说明这将被翻译成:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
    VariableModifiersopt TargetType Identifier =
        (TargetType) #i.next();
    Statement
}

所以这里的相关内容是TargetType的解决方案。这也在JLS中定义:

  

如果Type(在FormalParameter生产中)是引用类型,则TargetType为Type

由于Object肯定是引用类型,因此TargetTypeObject,因此选中的广告代码应为Object,而不是Integer。< / p>

这是一个错误,这个线程中的其他人进一步证明了如果使用ecj(Eclipse的编译器)则不会发生此问题。但是,我知道这对Oracle编译器团队来说是一个低优先级的错误,因为你必须滥用泛型来运用它。人们几乎会说它是一个特征,而不是一个错误。

后续

要最终确认这是一个错误,这是针对此问题的现有错误报告:

另外,我应该注意两件事。 首先,我上面给出的JLS引用是最新的JLS,而且该部分实际上已经改为Java 7(以回应这个错误!)

以下是增强的语句应翻译为for Java 6 and earlier的内容:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
        VariableModifiersopt Type Identifier = #i.next();
   Statement
}

如您所见,此处指定了选中的广告代码。所以javac中的错误不在于它正在进行错误的强制转换,而是它正在执行任何强制转换

第二,在Java 7中,javac根据JLS SE 7规范正确编译代码(这是我上面引用的)。因此,以下代码有效:

List<String> list_str = new ArrayList<String>();
((List) list_str).add(new StringBuilder(" stringbuilder"));
for (CharSequence o : list_str) {
   System.out.println("o value is" + o);
}

正确强制转换为CharSequence,而不是String。我最初使用JDK 6进行编译,而不是JDK 7.

答案 1 :(得分:1)

代码

    for(Object o : list_int)
    {
      System.out.println("o value is"+o);
    }

相当于这样:

for (Iterator<Integer> it = list.iterator(); it.hasNext();) {
    Integer o = it.next();
    System.out.println("o value is"+o);
}

正如您所看到的,Iterator是通用的,因此将值转换为其参数类型(Integer我们的情况)。

因此,场景后面的行Integer o = it.next();执行以下操作:

Integer o = (Integer)it.next();

我认为现在很明显ClassCastException是如何产生的。实际上,您设法将字符串值插入到列表中,因此当it.next()返回您的字符串时,转换失败。

所以,问题“你是如何设法将字符串插入到int列表中”仍然存在。 答案是泛型是编译器的魔力。他们的另一个名字是擦除。 Java字节代码不包含有关列表类型的信息。它只需要在需要时包含浇注到混凝土类型。这就是您设法将参数化列表分配给原始列表然后将sting添加到其中的原因。

正如你正确提到的,你看到了警告。结论是“不要忽视编译警告。”