pul*_*thi 8 java generics foreach classcastexception
语言:Java
编译器版本:1.6
在下面的代码中,我尝试执行以下操作:
List<String>StringList<String>到原始ListList<Integer>List给List<Integer>Integerget()@index 1和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)
Run Code Online (Sandbox Code Playgroud)
这是我的代码
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);
//}
}
}
Run Code Online (Sandbox Code Playgroud)
我认为这是一个编译器错误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
Run Code Online (Sandbox Code Playgroud)
但是,Java语言规范(14.14.2)表明此强制转换应该是Object,而不是Integer.它首先通过语法定义术语:
EnhancedForStatement:
for ( FormalParameter : Expression ) Statement
FormalParameter:
VariableModifiersopt Type VariableDeclaratorId
VariableDeclaratorId:
Identifier
VariableDeclaratorId []
Run Code Online (Sandbox Code Playgroud)
所以在我们的例子中,Type是Object.然后它继续说明这被转化为:
for (I #i = Expression.iterator(); #i.hasNext(); ) {
VariableModifiersopt TargetType Identifier =
(TargetType) #i.next();
Statement
}
Run Code Online (Sandbox Code Playgroud)
所以这里有关的是解决方案TargetType.这也在JLS中定义:
如果Type(在FormalParameter生产中)是引用类型,则TargetType是Type
由于Object肯定是引用类型,则TargetType是Object等的检查转换应该是Object,不Integer.
在这个线程中的其他人进一步证明了这是一个错误,并指出如果使用ecj(Eclipse的编译器)则不会发生这个问题.但是,我知道这对Oracle编译器团队来说是一个低优先级的错误,因为你必须滥用泛型来运用它.人们几乎会说这是一个功能,而不是一个bug.
为了最终确认这是一个错误,这是针对此确切问题的现有错误报告:
另外,我应该注意两件事. 首先,我在上面给出的JLS引用是在最新的JLS中,并且该部分实际上已经针对Java 7进行了更改(以回应此错误!)
以下是针对Java 6及更早版本应该转换为增强的for语句的内容:
for (I #i = Expression.iterator(); #i.hasNext(); ) {
VariableModifiersopt Type Identifier = #i.next();
Statement
}
Run Code Online (Sandbox Code Playgroud)
如您所见,此处未指定任何选中的强制转换.因此,错误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);
}
Run Code Online (Sandbox Code Playgroud)
有正确的演员CharSequence,不是String.我最初使用JDK 6进行编译,而不是JDK 7.