2011-05-02 7 views
6

Por favor, dame una pista sobre lo que está pasando aquí:genéricos de fantasía captura de colisión

List<? extends Number> a = new ArrayList<Number>(); 
List<? extends Number> b = new ArrayList<Number>(); 

a.addAll(b); // ouch! compiler yells at me, see the block below: 
/* 
    incompatible types 
    found : java.util.List<capture#714 of ? extends java.lang.Number> 
    required: java.util.List<java.lang.Number> 
*/ 

Este simple código no compila. Recuerdo vagamente algo relacionado con las capturas de tipo, como las que deberían usarse principalmente en las especificaciones de la interfaz, no el código real, pero nunca me quedé estupefacto.

Por supuesto, esto podría ser fijo bruto-fuerza, al igual que:

List<? extends Number> a = new ArrayList<Number>(); 
List<? extends Number> b = new ArrayList<Number>(); 

@SuppressWarnings({"unchecked"}) 
List<Number> aPlain = (List<Number>) a; 
@SuppressWarnings({"unchecked"}) 
List<Number> bPlain = (List<Number>) b; 

aPlain.addAll(bPlain); 

es así, no me realmente que o bien renunciar a las capturas en la declaración (la captura me vino de una interfaz, así que tendré que cambiar alguna API), o seguir con los moldes de tipo con anotaciones de supresión (que generalmente apestan y complican un poco el código).

+0

Hola, ¿por qué no declarar 'a' 'como Lista a = new ArrayList () '? – Axel

+0

Bueno, tengo una API que ya declara esta captura cuando me devuelve una lista. Utilizo un constructor solo para simplificar las cosas y hacerlas un poco más oscuras (desinteresadamente). –

Respuesta

9

Usted tiene esencialmente dos listas de posiblemente diferentes tipos. Porque ? extends Number significa una clase que se extiende Number. Así que para la lista a puede ser classA y para la lista b que puede ser, por ejemplo classB. No son compatibles, pueden ser totalmente diferentes.

+1

Eres malo ... dar un comentario como señuelo. Quería responder a ese comentario antes de responder. Ahora el comentario se ha ido y usted respondió en su lugar. ;) – musiKk

+0

@musiKk - Jajajaja, todo estaba planeado;) –

+0

Esto es de alguna manera relacionado con el tipo de covarianza. Tal vez el ejemplo en sí mismo (operación add all) sea algo restrictivo, pero mi opinión es que debería poder realizar cualquier operación de recolección siempre que equals() y hashCode() estén definidos en los objetos que están en la colección. _UPD_: Y, en realidad, Java también es restrictiva en este aspecto: Soy incapaz de crear un comodín directamente: new ArrayList (); // ay! .. –

6

El problema es que si se utiliza List<? extends Number> en realidad se podría hacer:

List<? extends Number> a = new ArrayList<Integer>(); 
List<? extends Number> b = new ArrayList<Double>(); 

a.addAll(b); //ouch, would add Doubles to an Integer list 

El compilador no puede saber de List<? extends Number> lo que el parámetro de tipo real es por lo que no le permita realizar la operación de adición.

Tampoco debe convertir las listas a List<Number> si las tiene como parámetro, ya que podría tener una lista de objetos Entero y agregarle objetos Dobles.

En ese caso, es mejor crear una nueva List<Number> y agregar los objetos de ambas listas:

List<Number> c = new ArrayList<Number>(a.size() + b.size()); 
c.addAll(a); 
c.addAll(b); 

Editar: en caso de que crear dos listas a nivel local, no lo neet el comodín ? de todos modos (ya que' d siempre tiene List<Number>).

+0

Uhm ... el código de solución no era correcto en absoluto. La idea es que obtenga dos listas con una captura, así que grabé ese código para reflejar la idea. –

+1

eliminado mi comentario sobre la "solución" para evitar interpretaciones erróneas – Thomas

3

No utilizar el comodín ?. Significa "Algunos tipos específicos que no sé", y como no conoce el tipo, no puede agregar nada a la lista. Cambie su código para usar List<Number> y todo funcionará.

Esto se está convirtiendo en la pregunta más frecuente de Java, en un centenar de variaciones ...

+0

bien, está bien, pero lo que son guías generales para el uso de comodines en absoluto? –

+1

@Anton: Mire aquí para una explicación más profunda con ejemplos: http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#What%20is%20a%20wildcard? Pero, en general, los comodines solo son útiles en algunos casos excepcionales. –

1

La cosa es:

List<? extends Number> a = new ArrayList<Number>(); 
List<? extends Number> b = new ArrayList<Number>(); 

también podría leerse como:

List<x extends Number> a = new ArrayList<Number>(); 
List<y extends Number> b = new ArrayList<Number>(); 

¿Cómo debe saber el compilador que xey son lo mismo?

+0

Bueno, estaba bastante seguro de que el compilador tiene toda la información que necesita. No es el caso... :) –

2

PECS (productor-se extiende, de consumo super)

  • no se puede poner cualquier cosa en un tipo declarado con un EXTIENDE comodín excepto por el valor null, que pertenece a cada tipo de referencia
  • No se puede sacar nada de un tipo declarado con un comodín de sUPER excepto por un valor de tipo de objeto, que es un tipo estupendo de cada tipo de referencia
Cuestiones relacionadas