Genéricos de Java (comodines)

109

Tengo un par de preguntas sobre comodines genéricos en Java:

  1. ¿Cuál es la diferencia entre List<? extends T>y List<? super T>?

  2. ¿Qué es un comodín acotado y qué es un comodín ilimitado?

Pablo fernandez
fuente
En el caso de que se elimine la respuesta de Ted Gao (ya que era solo de enlace), aquí está la publicación de blog a la que se vinculó.
Royhowie

Respuestas:

123

En su primera pregunta, <? extends T>y <? super T>son ejemplos de comodines acotados. Un comodín ilimitado se parece <?>y básicamente significa <? extends Object>. En términos generales, significa que el genérico puede ser de cualquier tipo. Un comodín acotado ( <? extends T>o <? super T>) impone una restricción al tipo al decir que tiene que extender un tipo específico ( <? extends T>se conoce como límite superior) o tiene que ser un ancestro de un tipo específico ( <? super T>se conoce como límite inferior) .

Los tutoriales de Java tienen explicaciones bastante buenas de los genéricos en los artículos comodines y más diversión con comodines .

Bill el lagarto
fuente
Solo para hacerlo bien, si A <B y B <C, entonces: ¿<A extiende C> está mal?
Pablo Fernandez
Si por A <B, quieres decir que A extiende B, entonces A extiende C. Sin embargo, no usarías eso en la sintaxis de comodines, dirías <? extiende C> para limitar sus opciones a A o B.
Bill the Lizard
y en ese caso si digo <? super C> ¿cuál sería la diferencia?
Pablo Fernandez
3
Solo quería recomendar otra referencia sobre Java Generics: angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Zach Scrivena
3
@Pablo: <? super C>significaría que su tipo está restringido a algo arriba Cen la jerarquía de tipos. (Perdón por la respuesta extremadamente tardía. Supongo que no tuvimos notificaciones de comentarios hace 2 años)
Bill the Lizard
49

Si tiene una jerarquía de clases A, B es una subclase de A, y C y D son subclase de B como a continuación

class A {}
class B extends A {}
class C extends B {}
class D extends B {}

Luego

List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();

List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile

public void someMethod(List<? extends B> lb) {
    B b = lb.get(0); // is fine
    lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}

public void otherMethod(List<? super B> lb) {
    B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
    lb.add(new B()); // is fine, as we know that it will be a super type of A 
}

Un comodín acotado es como ? extends Bdonde B es algún tipo. Es decir, el tipo es desconocido pero se le puede colocar un "límite". En este caso, está limitado por alguna clase, que es una subclase de B.

oxbow_lakes
fuente
¿Supongo que List<? super B>describe como List acepta el tipo que es la clase principal de la clase B ? ¿Por qué C y D no compilan hmm?
Volkan Güven
38

Josh Bloch también tiene una buena explicación de cuándo usar supery extendsen esta charla en video de google io donde menciona el mnemónico Producer extendsConsumersuper .

De las diapositivas de la presentación:

Suponga que desea agregar métodos masivos a Stack<E>

void pushAll(Collection<? extends E> src);

- src es un productor de E

void popAll(Collection<? super E> dst);

- dst es un consumidor de E

blanco
fuente
He leído el libro de Bloch, pero todavía no veo la diferencia entre extiende y super en este caso particular.
Pablo Fernandez
Mira el video, creo que está bastante claro. Además, creo que debería hacer otra pregunta sobre esto "¿cuál es la diferencia entre List <? Extiende T> y List <? Super T>", donde es de esperar que obtenga más respuestas. (si lo hace, agregue un enlace desde aquí)
blanco
2
Camas: ¿por qué no incluir el ejemplo en esta respuesta para que sea completo y autónomo? Los enlaces son efímeros.
James Schek
3

Puede haber ocasiones en las que desee restringir los tipos de tipos que se pueden pasar a un parámetro de tipo. Por ejemplo, es posible que un método que opera con números solo desee aceptar instancias de Number o sus subclases. Para esto son los parámetros de tipo acotado.

Collection<? extends MyObject> 

significa que puede aceptar todos los objetos que tienen una relación IS- A con MyObject (es decir, cualquier objeto que sea un tipo de myObject o podemos decir cualquier objeto de cualquier subclase de MyObject) o un objeto de la clase MyObject.

Por ejemplo:

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

Luego,

Collection<? extends MyObject> myObject; 

aceptará solo MyObject o hijos de MyObject (es decir, cualquier objeto de tipo OurObject o YourObject o MyObject, pero no ningún objeto de la superclase de MyObject).

Sandeep Kumar
fuente
1

En general,

Si una estructura contiene elementos con un tipo de formulario ? extends E, podemos sacar elementos de la estructura, pero no podemos poner elementos en la estructura.

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]"); 

Para poner elementos en la estructura, necesitamos otro tipo de comodín llamado Wildcards with super,

 List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
    List<Integer> ints = Arrays.asList(5, 6);
    Collections.copy(objs, ints);
    assert objs.toString().equals("[5, 6, four]");

    public static <T> void copy(List<? super T> dst, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) {
                dst.set(i, src.get(i));
         }
    }
Prateek Joshi
fuente
1

Los comodines genéricos se crean para hacer que los métodos que operan en Collection sean más reutilizables.

Por ejemplo, si un método tiene un parámetro List<A>, solo podemos darle List<A>a este método. Es un desperdicio para la función de este método en algunas circunstancias:

  1. Si este método solo lee objetos de List<A>, entonces se nos debería permitir dar List<A-sub>a este método. (Porque A-sub ES una A)
  2. Si este método solo inserta objetos en List<A>, entonces se nos debería permitir ceder List<A-super>a este método. (Porque A ES un A-super)
taoxiaopang
fuente