2011-03-23 4 views
28

¿Alguien sabe por qué no se compila el siguiente código? Ni add() ni addAll() funcionan como se esperaba. Eliminar la parte "? Extends" hace que todo funcione, pero no podría agregar subclases de Foo.Listas con comodines Causa Error genérico de vudú

List<? extends Foo> list1 = new ArrayList<Foo>(); 
List<? extends Foo> list2 = new ArrayList<Foo>(); 

/* Won't compile */ 
list2.add(new Foo()); //error 1 
list1.addAll(list2); //error 2 

de error 1:

IntelliJ dice:

add(capture<? extends Foo>) in List cannot be applied to add(Foo) 

El compilador dice:

cannot find symbol 
symbol : method addAll(java.util.List<capture#692 of ? extends Foo>) 
location: interface java.util.List<capture#128 of ? extends Foo> 

de error 2:

IntelliJ me da

addAll(java.util.Collection<? extends capture<? extends Foo>>) in List cannot be applied to addAll(java.util.List<capture<? extends Foo>>) 

Mientras que el compilador sólo dice

cannot find symbol 
symbol : method addAll(java.util.List<capture#692 of ? extends Foo>) 
location: interface java.util.List<capture#128 of ? extends Foo> 
     list1.addAll(list2); 
+3

favor explicar por qué se utiliza , en cambio solo Foo. Al explicar eso, tal vez las cosas se aclararán. – Ingo

+0

Hola, Ingo. Excelente punto, y uno que podría haber creado respuestas mejores/más precisas anteriormente. Estaba confundido por los parámetros del método usando comodines para aceptar subtipos, y pensé que necesitaba usar el para poder agregar subtipos a la colección. Myers y Paulo (abajo) ayudaron a entender por qué eso no es necesario (o correcto). Buen comentario, ¡gracias! – oligofren

Respuesta

41

(supongo que aquí Bar y Baz son ambos subtipos de Foo.)

List<? extends Foo> significa una lista de elementos de algún tipo, que es un subtipo de Foo, pero no sabemos qué tipo . Ejemplos de tales listas serían ArrayList<Foo>, LinkedList<Bar> y ArrayList<Baz>.

Como no sabemos qué subtipo es el parámetro de tipo, no podemos poner Foo objetos en ella, ni Bar o Baz objetos. Pero aún ahora que el parámetro tipo es un subtipo de Foo, por lo que cada elemento ya en la lista (y que podemos obtener de la lista) debe ser un objeto Foo, así que podemos usar Foo f = list.get(0); y cosas similares.

Dicha lista solo se puede utilizar para sacar elementos de la lista, no para agregar elementos (excepto null, pero no sé si el compilador realmente lo permite).

A List<Foo> en el otro lado permite añadir cualquier objeto que es un objeto Foo - y como Bar y Baz son subtipos de Foo, todos Bar y Baz objetos se Foo objetos, por lo que se pueden añadir, también.

+0

¡Gracias, Paulo! La respuesta vinculada de Michael Myers también fue muy directa, pero su respuesta no requirió un desvío :) – oligofren

20

Recuerde PECS: Producer Extends, Consumer Super.

Como está intentando agregar elementos a list2, es un consumidor y no se puede declarar como List<? extends Foo>. Pero también está usando list2 como productor cuando lo agrega a list1. Por lo tanto, list2 es productor y consumidor y debe ser List<Foo>.

list1, como consumidor puro, puede ser List<? super Foo>.

+0

Gracias por su ayuda, ¡la respuesta de PECS en la página vinculada es excelente! Votando por su respuesta vinculada, pero otorgando a Paulo el crédito por la respuesta aceptada, ya que no requirió un desvío para ser exhaustivo :) – oligofren

8

Hay errores. Permite modificar el código, teniendo en cuenta que Bar y Baz dos tipos diferentes que se extienden Foo:

List<? extends Foo> list1 = new ArrayList<Bar>(); 
List<? extends Foo> list2 = new ArrayList<Baz>(); 

Si list1.add(new Foo()) estaba permitido, se podría añadir instancias de Foo en una colección que contiene las instancias de barras. Esto explica el primer error.

Si se permitió list1.addAll(list2), todas las instancias de Baz en list2 se agregarían a list1, que contiene solo instancias de Bar. Esto explica el segundo error.

2

Lo sentimos, tal vez no he entendido bien su pregunta, pero suposing:

public class Bar extends Foo{ } 

este código:

List<Foo> list2 = new ArrayList<Foo>() 
list2.add(new Bar()); 

no generan ningún error para mí.

Por lo tanto, eliminar el comodín permite agregar subclases de Foo.

+0

En la pregunta, 'Lista 'se usa como tipo de variable, no' Lista ':-) –

+0

@Vivien: Lo sé. Lo que afirmo es que el comodín puede eliminarse para que el código se compile. –

+0

Y permitirá agregar subclases de Foo. –

3

Vamos a tratar de explicar en qué caso puede que tenga que utilizar <? extend Classname>.

Por lo tanto, digamos que usted tiene 2 clases:

class Grand { 
    private String name; 

    public Grand(String name) { 
     this.setName(name); 
    } 

    public Grand() { 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 
} 

class Dad extends Grand { 
    public Dad(String name) { 
     this.setName(name); 
    } 

    public Dad() { 
    } 
} 

Y digamos que usted tiene 2 colecciones, cada una contiene algunos Grands y algunos papás:

List<Dad> dads = new ArrayList<>(); 
    dads.add(new Dad("Dad 1")); 
    dads.add(new Dad("Dad 2")); 
    dads.add(new Dad("Dad 3")); 


    List<Dad> grands = new ArrayList<>(); 
    dads.add(new Dad("Grandpa 1")); 
    dads.add(new Dad("Grandpa 2")); 
    dads.add(new Dad("Grandpa 3")); 

Ahora, vamos a ASUME que queremos para tener una colección, que contendrá objetos Grand o Dad:

 List<Grand> resultList; 
     resultList = dads; // Error - Incompatable types List<Grand> List<Dad> 
     resultList = grands;//Works fine 

¿Cómo podemos evitar esto? Sólo tiene que utilizar comodines:

List<? extends Grand> resultList; 
resultList = dads; // Works fine 
resultList = grands;//Works fine 

Aviso, que no se puede agregar nuevos elementos en dicha colección (resultList). Para más información se puede leer sobre Comodín y conseption PECS en Java