Pregunta

¿En qué circunstancias puede ocurrir ClassCastException en el código siguiente:

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

Hemos tenido un caso similar en un entorno de producción (mala práctica, lo sé) y el cliente proporciona un registro con ClassCastException en la línea con el comentario, pero me parece que no puede reproducirlo. ¿Alguna idea?

Sé que la JVM crea un iterador en el fondo cuando se utiliza foreach, pero puedo crear un iterador que prima en algunos casos y una parametrizada en otros casos?

Actualizar También tuve un vistazo al código de bytes generado y en Windows, usando JDK 1.6.0_21-b07 sin checkcast fue hecho. Interesante :)

Este es el método principal:

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

Gracias a todos por las respuestas!

Actualización 2 : Tengo engañar con Eclipse IDE que utiliza su propio compilador lo que en realidad el código de bytes por encima de ella es la generada mediante el Eclipse compilador . Mira aquí cómo compilar código manualmente con Eclipse. En conclusión Eclipse compilador genera el código de bytes diferente del compilador de Sun en algunos casos, independientemente de la plataforma, el caso descrito aquí ser uno.

¿Fue útil?

Solución

En caso de que no código siempre lanzar una ClassCastException? Se hace por mí usando el compilador de Sun Java 6 y tiempo de ejecución (en Linux). Estás fundición Integers como Strings. El iterador creada será una Iterator<String>, pero entonces se trata de acceder al primer elemento, que es un Integer, y así se produce un error.

Esto queda más claro si cambia la matriz de esta manera:

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

Ahora el bucle realmente funciona para el primer elemento, debido a que el primer elemento es un String y vemos la salida; entonces el Iterator<String> produce un error en el segundo, porque no es una cadena.

Su código funciona si sólo se utiliza un List genérico en lugar de uno específico:

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

... o, por supuesto, si se utiliza List<Integer>, ya que los contenidos son Integers. Lo que está haciendo ahora desencadena una advertencia del compilador - Note: Generics.java uses unchecked or unsafe operations. -. Y por una buena razón

Esta modificación también funciona:

for (Object o : (List)list)

... presumiblemente porque en ese punto que está tratando con un Iterator, no un Iterator<String>.

bozho ha dicho que no ve este error en Windows XP (no mencionó los cuales compilador y tiempo de ejecución, pero supongo Sun), y usted dice que usted no está viendo (o no fiable), por lo claramente hay algo de sensibilidad aplicación aquí, pero la conclusión es: no utilice List<String> para interactuar con un List de Integers. : -)

Éste es el fichero Estoy 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);
        }
    }
}

Aquí está la compilación:

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

Aquí está la carrera:

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 línea 12 es la declaración for. Tenga en cuenta que lo hizo el primer elemento de salida, porque he cambiado eso a un String. No fue así dar salida a los otros. (Y antes de que hiciera ese cambio, no logró inmediatamente.)

Este es el compilador que estoy 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*

Aquí está el desmontaje, que muestra 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

}

Una vez más, sin embargo, el resultado final tiene que ser: no utilice un List<String> para interactuar con un List que contiene cosas que no son Strings. : -)

Otros consejos

No se puede reproducir tampoco, pero me detectar los siguientes errores que deben corregirse:

  • getObjects() devolverlo List<Integer>, en lugar de un tipo de prima
  • No hay que esperar List<String>, pero una vez List<Integer>
  • Cuando la iteración, for (Integer o : list) bucle

El problema es la static List getObjects() { método devuelve un List genérico (no parametrizado). Y lo ha asignado a List<String>. Esta línea debe dar una advertencia del compilador, y que debería haber señalado un problema.

Esta parte:

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

¿Se puede simplificar como

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

Sin embargo, la implementación de iterador intentará fundido al llamar al método next() el envío por el contenido de Iterator en un String.

Es por eso que tienen un CCE.

Soluciones:

genéricos

O bien utilizar todas partes o no los usa, pero es muy importante ser constante. Si había vuelto un List<Integer> o una List<? super Integer> que habría visto este problema en tiempo de compilación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top