2010-05-05 11 views
108

Tengo una lista que se declara así:¿Cómo puedo agregar a List <? se extiende Número> estructuras de datos?

List<? extends Number> foo3 = new ArrayList<Integer>(); 

He intentado añadir a 3 foo3. Sin embargo recibo un mensaje de error como este:

The method add(capture#1-of ? extends Number) in the type List<capture#1-of ? 
extends Number> is not applicable for the arguments (ExtendsNumber) 
+43

Tenga en cuenta que 'List 'no significa 'lista de objetos de diferentes tipos, todos los cuales extienden' Número' ". Significa "lista de objetos de un _solo_ tipo que extiende' Número' ". –

+1

Google 'Capture Conversion' para saber qué significa el error/advertencia. –

+1

Será mejor que compruebe la regla de PECS, Producer extends, Consumer super. http://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super – noego

Respuesta

225

Lo sentimos, pero no se puede.

La declaración comodín de List<? extends Number> foo3 significa que la variable foo3 puede contener cualquier valor de una familia de tipos (en lugar de cualquier valor de un tipo específico). Esto significa que ninguna de estas son las asignaciones legales:

List<? extends Number> foo3 = new ArrayList<Number>; // Number "extends" Number 
List<? extends Number> foo3 = new ArrayList<Integer>; // Integer extends Number 
List<? extends Number> foo3 = new ArrayList<Double>; // Double extends Number 

Así que, dado esto, ¿qué tipo de objeto podría agregar a List foo3 que sería legal después de cualquiera de los anteriores posibles ArrayList asignaciones:

  • No puede agregar un Integer porque foo3 podría estar apuntando a List<Double>.
  • No puede agregar un Double porque foo3 podría estar apuntando a un List<Integer>.
  • No puede agregar un Number porque foo3 podría estar apuntando a un List<Integer>.

No se puede agregar a cualquier objeto List<? extends T> porque no se puede garantizar qué tipo de List realmente está apuntando a, por lo que no se puede garantizar que el objeto esté previsto en ese List. La única "garantía" es que solo puede leer de ella y obtendrá una T o subclase de T.

La lógica inversa se aplica a super, p. Ej. List<? super T>. Estos son legales:

List<? super Number> foo3 = new ArrayList<Number>; // Number is a "super" of Number 
List<? super Number> foo3 = new ArrayList<Object>; // Object is a "super" of Number 

no puede leer el tipo T específicas (por ejemplo Number) de List<? super T> porque no se puede garantizar qué tipo de List realmente está apuntando. La única "garantía" que tiene es que puede agregar un valor de tipo T (o cualquier subclase de T) sin violar la integridad de la lista a la que se está apuntando.


El ejemplo perfecto de esto es la firma de Collections.copy():

public static <T> void copy(List<? super T> dest,List<? extends T> src) 

Note como la declaración src lista utiliza extends que me permita pasar cualquier lista de una familia de tipos de lista correspondiente y aún garantiza que producirá valores de tipo T o subclases de T. Pero no puede agregar a la lista src.

La declaración de lista dest usa super para permitirme pasar cualquier Lista de una familia de Tipos de Lista relacionados y aún así garantizar que puedo escribir un valor de un tipo específico T en esa lista. Pero no se puede garantizar que lea los valores de específicos tipo T si leo de la lista.

Así que ahora, gracias a los genéricos comodines, lo puede hacer cualquiera de estas llamadas con ese solo método:

// copy(dest, src) 
Collections.copy(new ArrayList<Number>(), new ArrayList<Number()); 
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer()); 
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>()); 
Collections.copy(new ArrayList<Object>(), new ArrayList<Double()); 

Considere este código confuso y muy amplia para ejercer su cerebro. Las líneas comentadas son ilegales y la razón está indicada en el extremo derecho de la línea (necesita desplazarse para ver algunas de ellas):

List<Number> listNumber_ListNumber = new ArrayList<Number>(); 
//List<Number> listNumber_ListInteger = new ArrayList<Integer>();     // error - can assign only exactly <Number> 
//List<Number> listNumber_ListDouble = new ArrayList<Double>();      // error - can assign only exactly <Number> 

    List<? extends Number> listExtendsNumber_ListNumber = new ArrayList<Number>(); 
    List<? extends Number> listExtendsNumber_ListInteger = new ArrayList<Integer>(); 
    List<? extends Number> listExtendsNumber_ListDouble = new ArrayList<Double>(); 

    List<? super Number> listSuperNumber_ListNumber = new ArrayList<Number>(); 
//List<? super Number> listSuperNumber_ListInteger = new ArrayList<Integer>();  // error - Integer is not superclass of Number 
//List<? super Number> listSuperNumber_ListDouble = new ArrayList<Double>();  // error - Double is not superclass of Number 


//List<Integer> listInteger_ListNumber = new ArrayList<Number>();     // error - can assign only exactly <Integer> 
    List<Integer> listInteger_ListInteger = new ArrayList<Integer>(); 
//List<Integer> listInteger_ListDouble = new ArrayList<Double>();     // error - can assign only exactly <Integer> 

//List<? extends Integer> listExtendsInteger_ListNumber = new ArrayList<Number>(); // error - Number is not a subclass of Integer 
    List<? extends Integer> listExtendsInteger_ListInteger = new ArrayList<Integer>(); 
//List<? extends Integer> listExtendsInteger_ListDouble = new ArrayList<Double>(); // error - Double is not a subclass of Integer 

    List<? super Integer> listSuperInteger_ListNumber = new ArrayList<Number>(); 
    List<? super Integer> listSuperInteger_ListInteger = new ArrayList<Integer>(); 
//List<? super Integer> listSuperInteger_ListDouble = new ArrayList<Double>();  // error - Double is not a superclass of Integer 


    listNumber_ListNumber.add(3);    // ok - allowed to add Integer to exactly List<Number> 

    // These next 3 are compile errors for the same reason: 
    // You don't know what kind of List<T> is really 
    // being referenced - it may not be able to hold an Integer. 
    // You can't add anything (not Object, Number, Integer, 
    // nor Double) to List<? extends Number>  
//listExtendsNumber_ListNumber.add(3);  // error - can't add Integer to *possible* List<Double>, even though it is really List<Number> 
//listExtendsNumber_ListInteger.add(3); // error - can't add Integer to *possible* List<Double>, even though it is really List<Integer> 
//listExtendsNumber_ListDouble.add(3);  // error - can't add Integer to *possible* List<Double>, especially since it is really List<Double> 

    listSuperNumber_ListNumber.add(3);  // ok - allowed to add Integer to List<Number> or List<Object> 

    listInteger_ListInteger.add(3);   // ok - allowed to add Integer to exactly List<Integer> (duh) 

    // This fails for same reason above - you can't 
    // guarantee what kind of List the var is really 
    // pointing to 
//listExtendsInteger_ListInteger.add(3); // error - can't add Integer to *possible* List<X> that is only allowed to hold X's 

    listSuperInteger_ListNumber.add(3);  // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object> 
    listSuperInteger_ListInteger.add(3);  // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object> 
+6

¿Por qué se nos permite escribir una oración como 'List foo3 = new ArrayList (); 'si no podemos seguir un elemento en esta lista? –

+6

@articlestack: Agregar no es lo único útil para hacer con una lista. Una vez que se completa una lista, la lectura de una lista puede ser útil. Los comodines genéricos permiten escribir código que funciona genéricamente para una familia de listas, p. funciona para la Lista o la Lista . Por ejemplo, mire la firma de Collection.copy(). El argumento 'src'' List' usa 'extends' para leer de la lista src, mientras que el argumento' dest' 'List' usa' super' para escribir en la lista dest. Esto permite un método que puede copiar de 'List ' o 'List ' en 'List ' o 'List '. –

+0

Bert F, disculpa por un comentario tardío. ¿Hay un error tipográfico en su pasaje, "para agregar un valor de tipo T (o subclase de T)" debe leer (o superclase de T)? – Vortex

13

No puede (sin moldes inseguros). Solo puedes leer de ellos.

El problema es que no se sabe exactamente de qué se trata la lista. Podría ser una lista de cualquier subclase de Número, de modo que cuando tratas de ponerle un elemento, no sabes que el elemento realmente cabe en la lista.

Por ejemplo, la lista podría ser una lista de Byte s, por lo que sería un error poner un Float en ella.

2

usted puede hacer esto en su lugar:

List<Number> foo3 = new ArrayList<Number>();  
    foo3.add(3); 
Cuestiones relacionadas