Question

Dans quelles circonstances peut ClassCastException se produire dans le code ci-dessous:

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);
        }
    }
}

Nous avons eu un cas similaire dans un environnement de production (mauvaise pratique, je sais) et le client a fourni un journal avec ClassCastException à la ligne avec le commentaire, mais je ne peux pas sembler le reproduire. Toute pensée?

Je sais que la machine virtuelle Java crée un itérateur en arrière-plan lors de l'utilisation foreach, mais peut-il créer une Iterator première dans certains cas et un dans d'autres cas paramétrisé?

Mise à jour : J'avais aussi un regard sur le bytecode généré et sous Windows, en utilisant JDK 1.6.0_21-b07 pas checkcast a été faite. Intéressant :)

Voici la principale méthode:

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

Merci pour toutes les réponses!

Mise à jour 2 : Je suis avec Eclipse IDE induits en erreur qui utilise son propre compilateur donc en fait le bytecode au-dessus est celui généré en utilisant le Eclipse compilateur . Regardez ici comment compiler manuellement le code avec Eclipse. En conclusion compilateur Eclipse génère différents byte-code du compilateur Sun dans certains cas, quelle que soit la plate-forme, le cas décrit ici étant l'un.

Était-ce utile?

La solution

ne doit pas que le code toujours jeter un ClassCastException? Il fait pour moi en utilisant le compilateur Sun Java 6 et exécution (sous Linux). Vous coulée Integers comme Strings. Le iterator créé sera un Iterator<String>, mais il tente d'accéder au premier élément, qui est un Integer, et il échoue.

Cela devient plus clair si vous changez votre tableau comme ceci:

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

Maintenant, la boucle fonctionne réellement pour le premier élément, parce que le premier élément est un String et nous voyons la sortie; alors le Iterator<String> échoue sur le second, parce qu'il est pas une chaîne.

Votre code fonctionne si vous suffit d'utiliser un List générique au lieu d'un spécifique:

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

... ou, bien sûr, si vous utilisez List<Integer>, puisque le contenu est Integers. Ce que vous faites déclenche maintenant un avertissement du compilateur - Note: Generics.java uses unchecked or unsafe operations. -. Et pour cause

Cette modification fonctionne aussi:

for (Object o : (List)list)

... sans doute parce qu'à ce moment-là que vous avez affaire à un Iterator, pas Iterator<String>.

bozho a dit qu'il ne voit pas cette erreur sous Windows XP (n'a pas mentionné le compilateur et l'exécution, mais je devine Sun), et vous dites que vous n'êtes pas le voir (ou non fiable), donc clairement, il y a une certaine sensibilité à la mise en œuvre, mais la ligne de fond est la suivante: ne pas utiliser List<String> pour interagir avec un List de Integers. : -)

Voici le fichier que je suis la compilation:

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);
        }
    }
}

Voici la compilation:

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

Voici la course:

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)

La ligne 12 est la déclaration de for. Notez qu'il a sortie le premier élément, parce que je changé cela à un String. Il n'a pas émis les autres. (Et avant que je fait ce changement, il n'a pas immédiatement.)

Voici le compilateur que je utilise:

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*

Voici le démontage, qui montre le 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

}

Encore une fois, cependant, la ligne de fond doit être: Ne pas utiliser un List<String> pour interagir avec un List qui contient des choses qui ne sont pas Strings. : -)

Autres conseils

Je ne peux pas reproduire non plus, mais je repérer les erreurs suivantes qui doivent être corrigées:

  • retour getObjects() List<Integer>, plutôt qu'un type cru
  • Ne vous attendez pas List<String>, mais au lieu List<Integer>
  • Lorsque itérer for (Integer o : list) boucle

Le problème est la méthode static List getObjects() { renvoie un générique (non paramétrée) List. Et vous attribuez à List<String>. Cette ligne doit donner un avertissement du compilateur, et qui aurait dû signaler un problème.

Cette partie:

List<String> list = getObjects();
for (Object o : list) { // ClassCastException?
    System.out.println(o);
}

sera simplifiée comme

List<String> list = getObjects();
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    Object o = iterator.next();
    System.out.println(o);
}

Mais la mise en œuvre de Iterator va essayer de plâtre lors de l'appel de la méthode next() l'envoi de contenu par le Iterator dans un String.

C'est la raison pour laquelle vous avez un CCE.

Solutions:

Soit l'utilisation des génériques partout ou ne les utilisent pas, mais il est vraiment important d'être cohérente. Si vous avez retourné un List<Integer> ou List<? super Integer> vous auriez vu ce problème au moment de la compilation.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top