语言:Java
编译器版本:1.6
在下面的代码中,我尝试执行以下操作:
List<String>
String
List<String>
分配给原始List
List<Integer>
List
分配给List<Integer>
Integer
get()
@index 1&amp; 2并打印出来。所有语句都在编译(带警告)并运行正常。
但如果我尝试使用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);
//}
}
}
答案 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 []
因此,在我们的案例中,Type
是Object
。然后它继续说明这将被翻译成:
for (I #i = Expression.iterator(); #i.hasNext(); ) {
VariableModifiersopt TargetType Identifier =
(TargetType) #i.next();
Statement
}
所以这里的相关内容是TargetType
的解决方案。这也在JLS中定义:
如果Type(在FormalParameter生产中)是引用类型,则TargetType为Type
由于Object
肯定是引用类型,因此TargetType
为Object
,因此选中的广告代码应为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添加到其中的原因。
正如你正确提到的,你看到了警告。结论是“不要忽视编译警告。”