Domanda

In quali circostanze può ClassCastException verificarsi nel seguente codice:

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

Abbiamo avuto un caso simile in un ambiente di produzione (cattiva pratica, lo so) e il cliente fornito un registro con ClassCastException in linea con il commento, ma io non riesco a riprodurlo. Ogni pensiero?

So che la JVM crea un iteratore in background quando si utilizza foreach, ma può creare un Iterator grezzo in alcuni casi e una parametrizzata in altri casi?

Aggiorna : Ho anche avuto uno sguardo al bytecode generato e su Windows, utilizzando JDK 1.6.0_21-b07 non checkcast è stato fatto. Interessante :)

Ecco il metodo principale:

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

Grazie a tutti per le risposte!

Aggiorna 2 : Ho ottenuto indurre in errore con Eclipse IDE che utilizza il suo proprio compilatore quindi in realtà il bytecode sopra è quello generato utilizzando il Eclipse compilatore . Guardate qui come compilare manualmente il codice con Eclipse. In conclusione Eclipse compilatore genera byte-codice diverso dal compilatore Sun in alcuni casi, indipendentemente dalla piattaforma, il caso descritto qui essere uno.

È stato utile?

Soluzione

non dovrebbero che il codice sempre gettare un ClassCastException? Lo fa per me usando il compilatore Sun Java 6 e di esecuzione (su Linux). Stai colata Integers come Strings. L'iteratore creato sarà un Iterator<String>, ma poi si tenta di accedere al primo elemento, che è un Integer, e così non riesce.

Questo diventa più chiaro se si cambia l'array in questo modo:

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

Ora il ciclo effettivamente funziona per il primo elemento, perché il primo elemento è un String e vediamo l'uscita; poi il Iterator<String> non riesce sul secondo, perché non è una stringa.

Il tuo codice funziona solo se si utilizza un List generica al posto di uno specifico:

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

... o, naturalmente, se si utilizza List<Integer>, dal momento che i contenuti sono Integers. Quello che stai facendo ora innesca un compilatore avvertimento - Note: Generics.java uses unchecked or unsafe operations. -. E per una buona ragione

Questa modifica funziona anche:

for (Object o : (List)list)

... presumibilmente perché a quel punto hai a che fare con una Iterator, non un Iterator<String>.

Bozho ha detto che non vede questo errore su Windows XP (non ha menzionato quale compilatore e runtime, ma sto cercando di indovinare Sun), e dire che non stiamo vedendo (o non affidabile), in modo da chiaramente c'è una certa sensibilità realizzazione qui, ma la linea di fondo è: non utilizzare List<String> di interagire con una List di Integers. : -)

Ecco il file Sto compilando:

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

Ecco la compilazione:

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

Ecco il percorso:

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)

Linea 12 è la dichiarazione for. Si noti che lo ha fatto in uscita il primo elemento, perché ho cambiato che a un String. Essa non ha uscita gli altri. (E prima ho fatto che il cambiamento, non è riuscito immediatamente.)

Ecco il compilatore che sto usando:

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*

Ecco il disassemblaggio, che mostra la 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

}

Anche in questo caso, però, la linea di fondo deve essere: non utilizzare un List<String> di interagire con una List che contiene cose che non sono Strings. : -)

Altri suggerimenti

Non riesco a riprodurre sia, ma ho posto i seguenti errori che devono essere corretti:

  • getObjects() make List<Integer> ritorno, piuttosto che un tipo grezzo
  • Non aspettatevi List<String>, ma un List<Integer> invece
  • Quando l'iterazione, ciclo for (Integer o : list)

Il problema è il metodo static List getObjects() { restituisce un generico (non parametrizzato) List. E si assegna a List<String>. Questa linea dovrebbe dare un avviso del compilatore, e che avrebbe dovuto segnalato un problema.

Questa parte:

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

sarà semplificata come

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

Ma l'attuazione di Iterator cercherà di getto quando si chiama il metodo next() l'invio di contenuti dal Iterator in un String.

È per questo che hanno una CCE.

Soluzioni:

In entrambi i generici uso in tutto il mondo o non li usano, ma è davvero importante essere coerente. Se tu avessi restituito un List<Integer> o un List<? super Integer> avreste visto questo problema in fase di compilazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top