2012-07-16 39 views
5

que estaba buscando en el Java Generics documentation y encontré esta pieza de código,Java Genéricos Lista de captura <?>

public class WildcardError { 

void foo(List<?> l) { 
    //This give a compile time error 
    l.set(0,l.get(0)); 
} 
} 

puedo entender que estamos ir a buscar un elemento de una List<?> y tratando de ponerlo en otra List<?>. Entonces el compilador da un error. Mi pregunta es que tiene sentido cuando las 2 listas son diferentes, es decir, l.set(0, m.get(0)) aquí listas l y m son diferentes. Pero en el ejemplo anterior, l y l son las mismas listas. ¿Por qué el compilador no es lo suficientemente inteligente como para ver eso? ¿Es difícil implementarlo?

Editar: Soy consciente de que puedo solucionarlo mediante un método de ayuda o mediante el uso de T en lugar de un ?. Me pregunto por qué el compilador no lo hace por mí.

Respuesta

4

El compilador informa de un error porque no hay manera, en general, de que pueda decir si dos expresiones (en este caso l y l) hacen referencia a la misma lista.

relacionada, un tanto generalizada, pregunta:

+0

Me he estado preguntando sobre una razón formal para esto durante mucho tiempo. Personalmente siento que esta es una característica que falta en el JLS, ya que los dos comodines en ambos 'l' son formalmente iguales. Al menos por intuición ... Pero probablemente, en piezas de código más complejas, sería más difícil reconocer comodines "idénticos" –

+0

Sí. Probablemente haya algunos casos de esquina interesantes que obligarían a la especificación a ser bastante compleja. Buena pregunta sin embargo. Es una especie de generalización de esta pregunta. Debe publicarlo como seguimiento. – aioobe

+1

Bien, esta es la pregunta: http://stackoverflow.com/questions/11500385/how-does-the-jls-justify-the-wildcards-cannot-bellyly-used-within-methods –

9

En su caso específico, se puede fijar explícitamente esta:

public class WildcardError { 
    <T> void foo(List<T> l) { 
     // This will work 
     l.set(0, l.get(0)); 
    } 
} 

O si no desea cambie la API original, introduzca un método de ayuda de delegado:

public class WildcardError { 
    void foo(List<?> l) { 
     foo0(l); 
    } 

    private <T> void foo0(List<T> l) { 
     // This will work 
     l.set(0, l.get(0)); 
    } 
} 

Desafortunadamente, el compilador no puede inferir el tipo "obvio" <T>. Me he estado preguntando sobre eso también. Parece algo que podría mejorarse en un compilador, ya que cada comodín puede traducirse informalmente a un tipo desconocido <T>. Probablemente, hay algunas razones por las cuales esto fue omitido, quizás esto sea solo intuición, pero formalmente imposible.

ACTUALIZACIÓN:

Nota, acabo de ver esta peculiar aplicación de Collections.swap():

public static void swap(List<?> list, int i, int j) { 
    final List l = list; 
    l.set(i, l.set(j, l.get(i))); 
} 

Los chicos JDK recurren a un tipo de crudo, con el fin de manejar esto, a nivel local. Esta es una fuerte declaración que indica que esto probablemente se debe estar soportado por el compilador, pero por alguna razón (por ejemplo, la falta de tiempo para especificar formalmente) simplemente no se hizo

+1

Sí, conozco otras alternativas para solucionar esto. Me pregunto por qué el compilador no puede hacer eso solo cuando ambas listas son iguales. –

+1

Sí, me pregunto eso también. Me hubiera impedido escribir cientos de métodos de delegación inútiles en el pasado ...: -/Tal vez debería actualizar su pregunta y solicitar una explicación específica de [JLS] (http://docs.oracle.com/ javase/specs/jls/se7/html/index.html), lo que explica esto. –

+0

Gracias por editar la publicación :). ¿Dónde puedo preguntarle a JLS? –

2

List<?> significa lista que contiene los elementos de un desconocido escriba, así que cuando uno quiere tomar elementos de él usando list.get(i), devolverá el objeto de un tipo desconocido, por lo que la única conjetura válida será Object. Entonces, cuando uno trata de establecer elemento de espalda usando list.set(index, list.get(index)) se produce en tiempo de compilación de error, ya que como se mencionó anteriormente List<?> sólo puede contener algún tipo desconocido , por lo que poner Object a que puede causar ClassCastException.

Esto se explica muy bien en Effective Java de Joshua Bloch, 2ª ed, artículo 28:. Uso limitado comodines para aumentar API flexibilidad

Esto también se conoce como PECS principio y buena explicación se puede encontrar en este Q/a: What is PECS (Producer Extends Consumer Super)? (tenga en cuenta que List<?> es lo mismo que List<? extends Object> con pequeñas excepciones)

En términos del hombre común, se debe utilizar como parámetro de método List<?> sólo para obtener elementos desde dentro de ese método, no cuando uno necesita poner los elementos en la lista. Cuando se necesita tanto poner y obtener, necesita o bien generar el método utilizando el parámetro de tipo T como en la respuesta de Lukas Eder (modo seguro) o simplemente usar List<Object> (no de forma segura).

Cuestiones relacionadas