Java 迭代参数化列表(在原始列表类型赋值之后)
语言:JavaJava 迭代参数化列表(在原始列表类型赋值之后),java,generics,classcastexception,foreach,Java,Generics,Classcastexception,Foreach,语言:Java 编译器版本:1.6 在下面的代码中,我尝试执行以下操作: 创建一个列表 添加一个字符串 将列表分配给原始列表 创建一个列表 将原始列表分配给列表 添加一个整数 使用get()@索引1和2检索值并打印它们 所有语句都在编译(带有警告)并正常运行 但是如果我尝试使用for循环遍历列表,我会得到一个ClassCastException。我只是想知道为什么它允许我使用list.get()方法,但不允许我迭代它 输出:(如果我使用未注释的for循环运行)abcd 200 这是我的密码 im
编译器版本:1.6 在下面的代码中,我尝试执行以下操作:
列表
字符串
列表
分配给原始列表
列表
列表
分配给列表
整数
get()
@索引1和2检索值并打印它们for
循环遍历列表
,我会得到一个ClassCastException
。我只是想知道为什么它允许我使用list.get()
方法,但不允许我迭代它
输出:(如果我使用未注释的for循环运行)abcd 200
这是我的密码
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);
//}
}
}
import java.util.*;
导入java.io.*;
类CheckRawTypeAdd
{
公共静态void main(字符串[]sr)
{
List_str=new ArrayList();
添加列表(“abcd”);
List_int=new ArrayList();
原始列表;
list_raw=list_str;
list_int=list_raw;
补充清单(200);
对象o1=list_int.get(0);
对象o2=list_int.get(1);
系统输出打印项次(o1);
系统输出打印Ln(o2);
//用于(对象o:list_int)
//{
//System.out.println(“o值为”+o);
//}
}
}
代码
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);
}
for(Iterator it=list.Iterator();it.hasNext();){
整数o=it.next();
System.out.println(“o值为”+o);
}
如您所见,Iterator
是泛型的,因此将值强制转换为其参数类型(Integer
我们的案例)
因此,行整数o=it.next()代码>幕后执行以下操作:
Integer o=(Integer)it.next()代码>
我认为现在很明显,ClassCastException
是如何产生的。实际上,您成功地将字符串值插入到列表中,因此当it.next()
返回字符串时,转换失败
所以,“您是如何将字符串插入int列表的”这个问题仍然存在。
答案是泛型是编译器的魔力。他们的另一个名字是擦除。Java字节码不包含有关列表类型的信息。它只包含在需要时浇铸成混凝土类型。这就是您设法将参数化列表分配给原始列表,然后向其中添加sting的原因
正如您正确提到的,您看到了警告。结论是“不要忽视编译警告”。 < P>我会认为这是一个编译器错误,在代码> javac 。正在插入选中的强制类型转换。我们可以使用javap-c CheckRawTypeAdd
来反汇编类(cast是101;注意,我在编译之前去掉了一些不需要的代码行,因此代码点会有所不同):
但是,指示此强制转换应为对象
,而不是整数
。首先通过语法定义术语:
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
由于对象
肯定是引用类型,那么目标类型
就是对象
,因此选中的强制转换应该是对象
,而不是整数
此线程中的其他人进一步证明了这是一个bug,并指出,如果使用ecj
(Eclipse的编译器),则不会出现此问题。但是,我知道这对于Oracle编译器团队来说是一个低优先级的bug,因为您必须滥用泛型来执行它。人们几乎会说这是一个功能,而不是一个bug
随访
为了最终确认这是一个bug,这里有一个关于这个问题的现有bug报告:
另外,我应该注意两件事首先,我上面给出的JLS参考是在最新的JLS中,而Java 7的这一部分实际上已经改变了(为了响应这个bug!)
以下是增强型for语句应该翻译成的内容:
如您所见,这里没有指定选中的强制转换。因此,javac
中的缺陷不是它做了错误的转换,而是它做了任何转换
Second,在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);
}
List\u str=new ArrayList();
添加(新的StringBuilder(“StringBuilder”);
用于(字符序列o:列表\u str){
System.out.println(“o值为”+o);
}
将正确的转换为字符序列
,而不是字符串
。我最初使用的是JDK 6进行编译,而不是JDK 7。密切相关:@assylias,我尝试了搜索功能,但无法访问此链接。我现在正在经历它。谢谢。这是一个轶事,但为什么它会是整数o=it.next()
而不是对象o=it.next()
?同样与您所说的相反,Iterator
不强制转换任何内容,因为泛型是一个仅编译时的构造。它运行时没有任何异常。我想OP在for循环中用对象
替换了整数
。@Bhesh,不,我遇到了运行时故障。您正在使用Eclipse编译器吗?我在使用javac。@MarkPeters:是的,我在使用Eclipse编译器。+1回答得很好。我错过了部分如果Type(在FormalParameter产品中)是参考类型,那么TargetType是Type
for (I #i = Expression.iterator(); #i.hasNext(); ) {
VariableModifiersopt TargetType Identifier =
(TargetType) #i.next();
Statement
}
for (I #i = Expression.iterator(); #i.hasNext(); ) {
VariableModifiersopt Type Identifier = #i.next();
Statement
}
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);
}