Java中的ClassCastException foreach循环 [英] ClassCastException in Java foreach loop

查看:186
本文介绍了Java中的ClassCastException foreach循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中会出现ClassCastException:
$ b $ pre $ import $ java.util.Arrays;
import java.util.List;

public class Generics {

static List getObjects(){
return Arrays.asList(1,2,3);


public static void main(String [] args){
List< String> list = getObjects();
for(Object o:list){// ClassCastException?
System.out.println(o);





我们有类似的情况一个生产环境(不好的做法,我知道)和客户提供了与评论线ClassCastException的日志,但我似乎无法重现它。任何想法?我知道JVM在使用foreach的时候会在后台创建一个迭代器,但是在某些情况下它可以创建一个原始的Iterator,而在其他情况下它可以创建一个原始的Iterator我还看了一下在Windows上生成的字节码,使用JDK 1.6.0_21-b07没有 checkcast 。有趣的:)



以下是主要方法:

 
public static void main(java.lang.String []);
代码:
0:invokestatic#34; //方法getObjects :()Ljava / util / List;
3:astore_1
4:aload_1
5:invokeinterface#36,1; // InterfaceMethod java / util / List.iterator :()Ljava / util / Iterator;
10:astore_3
11:goto 28
14:aload_3
15:invokeinterface#42,1; // InterfaceMethod java / util / Iterator.next :()Ljava / lang / Object;
20:astore_2
21:getstatic#48; // java / lang / System.out:Ljava / io / PrintStream;
24:aload_2
25:invokevirtual#54; //方法java / io / PrintStream.println:(Ljava / lang / Object;)V
28:aload_3
29:invokeinterface#60,1; // InterfaceMethod java / util / Iterator.hasNext :()Z
34:ifne 14
37:return

感谢所有的答案!



更新2 :我误解了使用其自己的编译器,实际上是使用 Eclipse编译器生成的字节码。请查看此处如何使用Eclipse手动编译代码。
总而言之,在某些情况下,Eclipse编译器会在Sun编译器中生成不同的字节码,不管平台如何,这里描述的情况都是一样的。

方案

不应该总是 抛出 ClassCastException ?它为我使用Sun Java 6编译器和运行时(在Linux上)。您将整数 s转换为 String s。创建的迭代器将是一个 Iterator< String> ,但是它会尝试访问第一个元素,它是一个 Integer

pre

pre $ p $ > return Arrays.asList(one,2,3);

现在循环对第一个元素起作用,因为第一个元素是 String ,我们看到输出;那么 Iterator< String> 会在第二个失败,因为它不是一个字符串。

如果你只是使用一个通用的 List 而不是特定的:

  List list = getObjects(); 
for(Object o:list){
System.out.println(o);

...当然,如果您使用 List< Integer> ,因为内容是 Integer s。你现在正在做的事情触发了一个编译器警告 — 注意:Generics.java使用未经检查或不安全的操作。 —这是很有道理的。



这个修改也适用:

  for (Object o:(List)list)

...大概是因为在那个时候使用 Iterator ,而不是 Iterator< String>

bozho说他没有在Windows XP上看到这个错误(没有提到哪个编译器和运行库,但是我猜Sun是这样的),而且你说你看不到它(或者不可靠),所以显然这里有一些实现的敏感性,但底线是:不要使用 List< String> 列表交互 整数 s。 : - )



这是我正在编译的文件:

  import java.util.Arrays; 
import java.util.List;

public class Generics {

static List getObjects(){
return Arrays.asList(one,2,3);


public static void main(String [] args){
List< String> list = getObjects();
for(Object o:list){// ClassCastException?
System.out.println(o);





这里是汇编: p>

 tjc @ forge:〜/ temp $ javac Generics.java 
注意:Generics.java使用未经检查或不安全的操作。
注意:使用-Xlint重新编译:取消选中以查看详细信息。


运行:

< $ p $ t $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $'$'$'$'$'$'$'$'在Generics.main(Generics.java:12)

第12行是 for 声明。请注意,它确实输出了第一个元素,因为我将其更改为 String 。它没有输出其他的。 (在我做这个改变之前,它立即失败了。)

下面是我使用的编译器:

 tjc @ forge:〜/ temp $哪个javac 
/ usr / bin / javac
tjc @forge:〜/ temp $ ll / usr / bin / javac
lrwxrwxrwx 1 root root 23 2010-09-30 16:37 / usr / bin / javac - > / etc / alternatives / javac *
tjc @ forge:〜/ temp $ ll / etc / alternatives / javac
lrwxrwxrwx 1 root root 33 2010-09-30 16:37 / etc / alternatives / javac - > / usr / lib / jvm / java-6-sun / bin / javac *

这是反汇编,它显示了 checkcast

$ $ $ $ $伪造:〜/ temp $ javap -c Generics
从Generics.java编译
public class Generics extends java.lang.Object {
public Generics();
代码:
0:aload_0
1:invokespecial#1; //方法java / lang / Object。:(:)V
4:return

static java.util.List getObjects();
代码:
0:iconst_3
1:anewarray#2; // class java / io / Serializable
4:dup
5:iconst_0
6:ldc#3; //字符串1
8:aastore
9:dup
10:iconst_1
11:iconst_2
12:invokestatic#4; //方法java / lang / Integer.valueOf:(I)Ljava / lang / Integer;
15:aastore
16:dup
17:iconst_2
18:iconst_3
19:invokestatic#4; //方法java / lang / Integer.valueOf:(I)Ljava / lang / Integer;
22:aastore
23:invokestatic#5; //方法java / util / Arrays.asList:([Ljava / lang / Object;)Ljava / util / List;
26:areturn

public static void main(java.lang.String []);
代码:
0:invokestatic#6; //方法getObjects :()Ljava / util / List;
3:astore_1
4:aload_1
5:invokeinterface#7,1; // InterfaceMethod java / util / List.iterator :()Ljava / util / Iterator;
10:astore_2
11:aload_2
12:invokeinterface#8,1; //接口方法java / util / Iterator.hasNext :()Z
17:ifeq 40
20:aload_2
21:invokeinterface#9,1; // InterfaceMethod java / util / Iterator.next :()Ljava / lang / Object;
26:checkcast#10; // class java / lang / String
29:astore_3
30:getstatic#11; // java / lang / System.out:Ljava / io / PrintStream;
33:aload_3
34:invokevirtual#12; //方法java / io / PrintStream.println:(Ljava / lang / Object;)V
37:goto 11
40:return
$ b $} >

同样,底线必须是:不要使用 List< String> 来与 List ,它包含的内容不是 String s。 : - )

In what circumstances can ClassCastException occur in the code below:

import java.util.Arrays;
import java.util.List;

public class Generics {

    static List getObjects() {
        return Arrays.asList(1, 2, 3);
    }

    public static void main(String[] args) {
        List<String> list = getObjects();
        for (Object o : list) { // ClassCastException?
            System.out.println(o);
        }
    }
}

We had a similar case in a production environment (bad practice, I know) and the customer provided a log with ClassCastException at the line with the comment but I can't seem to reproduce it. Any thoughts?

I know that the JVM creates an iterator in the background when using foreach, but can it create a raw Iterator in some cases and a parametrized one in other cases?

Update:I also had a look at the bytecode generated and on Windows, using JDK 1.6.0_21-b07 no checkcast was made. Interesting :)

Here is the main method:

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #34; //Method getObjects:()Ljava/util/List;
   3:   astore_1
   4:   aload_1
   5:   invokeinterface #36,  1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
   10:  astore_3
   11:  goto    28
   14:  aload_3
   15:  invokeinterface #42,  1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
   20:  astore_2
   21:  getstatic   #48; //Field java/lang/System.out:Ljava/io/PrintStream;
   24:  aload_2
   25:  invokevirtual   #54; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   28:  aload_3
   29:  invokeinterface #60,  1; //InterfaceMethod java/util/Iterator.hasNext:()Z
   34:  ifne    14
   37:  return

Thanks all for the answers!

Update 2: I got mislead with Eclipse IDE that uses its own compiler so actually the bytecode above it's the one generated using the Eclipse compiler. Look here how to manually compile code with Eclipse. In conclusion Eclipse compiler generates different byte-code from the Sun compiler in some cases, regardless of the platform, the case described here being one.

解决方案

Shouldn't that code always throw a ClassCastException? It does for me using the Sun Java 6 compiler and runtime (on Linux). You're casting Integers as Strings. The iterator created will be an Iterator<String>, but then it tries to access the first element, which is an Integer, and so it fails.

This gets clearer if you change your array like so:

return Arrays.asList("one", 2, 3);

Now the loop actually works for the first element, because the first element is a String and we see the output; then the Iterator<String> fails on the second one, because it isn't a string.

Your code works if you just use a generic List instead of a specific one:

List list = getObjects();
for (Object o : list) {
    System.out.println(o);
}

...or, of course, if you use List<Integer>, since the contents are Integers. What you're doing now triggers a compiler warning — Note: Generics.java uses unchecked or unsafe operations. — and for good reason.

This modification also works:

for (Object o : (List)list)

...presumably because at that point you're dealing with an Iterator, not an Iterator<String>.

bozho has said he doesn't see this error on Windows XP (didn't mention which compiler and runtime, but I'm guessing Sun's), and you say you're not seeing it (or not reliably), so clearly there's some implementation sensitivity here, but the bottom line is: Don't use List<String> to interact with a List of Integers. :-)

Here's the file I'm compiling:

import java.util.Arrays;
import java.util.List;

public class Generics {

    static List getObjects() {
        return Arrays.asList("one", 2, 3);
    }

    public static void main(String[] args) {
        List<String> list = getObjects();
        for (Object o : list) { // ClassCastException?
            System.out.println(o);
        }
    }
}

Here's the compilation:

tjc@forge:~/temp$ javac Generics.java 
Note: Generics.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

Here's the run:

tjc@forge:~/temp$ java Generics 
one
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at Generics.main(Generics.java:12)

Line 12 is the for statement. Note that it did output the first element, because I changed that to a String. It didn't output the others. (And before I made that change, it failed immediately.)

Here's the compiler I'm using:

tjc@forge:~/temp$ which javac
/usr/bin/javac
tjc@forge:~/temp$ ll /usr/bin/javac
lrwxrwxrwx 1 root root 23 2010-09-30 16:37 /usr/bin/javac -> /etc/alternatives/javac*
tjc@forge:~/temp$ ll /etc/alternatives/javac
lrwxrwxrwx 1 root root 33 2010-09-30 16:37 /etc/alternatives/javac -> /usr/lib/jvm/java-6-sun/bin/javac*

Here's the disassembly, which shows the checkcast:

tjc@forge:~/temp$ javap -c Generics
Compiled from "Generics.java"
public class Generics extends java.lang.Object{
public Generics();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return

static java.util.List getObjects();
  Code:
   0:   iconst_3
   1:   anewarray   #2; //class java/io/Serializable
   4:   dup
   5:   iconst_0
   6:   ldc #3; //String one
   8:   aastore
   9:   dup
   10:  iconst_1
   11:  iconst_2
   12:  invokestatic    #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   15:  aastore
   16:  dup
   17:  iconst_2
   18:  iconst_3
   19:  invokestatic    #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   22:  aastore
   23:  invokestatic    #5; //Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
   26:  areturn

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #6; //Method getObjects:()Ljava/util/List;
   3:   astore_1
   4:   aload_1
   5:   invokeinterface #7,  1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
   10:  astore_2
   11:  aload_2
   12:  invokeinterface #8,  1; //InterfaceMethod java/util/Iterator.hasNext:()Z
   17:  ifeq    40
   20:  aload_2
   21:  invokeinterface #9,  1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
   26:  checkcast   #10; //class java/lang/String
   29:  astore_3
   30:  getstatic   #11; //Field java/lang/System.out:Ljava/io/PrintStream;
   33:  aload_3
   34:  invokevirtual   #12; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   37:  goto    11
   40:  return

}

Again, though, the bottom line has to be: Don't use a List<String> to interact with a List that contains things that aren't Strings. :-)

这篇关于Java中的ClassCastException foreach循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆