Diferencia entre y en Java

¿Cuál es la diferencia entre List List y List List ?

Solía ​​usar List List , pero no me permite agregarle elementos list.add(e) , mientras que List List hace.

extends

La statement de comodín de List< ? extends Number> foo3 List< ? extends Number> foo3 significa que cualquiera de estas son asignaciones legales:

 List< ? extends Number> foo3 = new ArrayList(); // Number "extends" Number (in this context) List< ? extends Number> foo3 = new ArrayList(); // Integer extends Number List< ? extends Number> foo3 = new ArrayList(); // Double extends Number 
  1. Lectura : dadas las posibles asignaciones anteriores, ¿qué tipo de objeto está garantizado para leer en la List foo3 :

    • Puede leer un Number porque cualquiera de las listas que podrían asignarse a foo3 contiene un Number o una subclase de Number .
    • No puede leer un Integer porque foo3 podría estar apuntando a una List .
    • No puede leer un Double porque foo3 podría estar apuntando a una List .
  2. Escritura : dadas las posibles asignaciones anteriores, ¿qué tipo de objeto podría agregar a la List foo3 que sería legal para todas las posibles asignaciones de ArrayList anteriores?

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

No puede agregar ningún objeto a la List< ? extends T> List< ? extends T> porque no puede garantizar a qué tipo de List apunta realmente, por lo que no puede garantizar que el objeto esté permitido en esa List . La única “garantía” es que solo puede leer de ella y obtendrá una T o subclase de T

super

Ahora considere List < ? super T> List < ? super T> .

La statement de comodín de List< ? super Integer> foo3 List< ? super Integer> foo3 significa que cualquiera de estas son asignaciones legales:

 List< ? super Integer> foo3 = new ArrayList(); // Integer is a "superclass" of Integer (in this context) List< ? super Integer> foo3 = new ArrayList(); // Number is a superclass of Integer List< ? super Integer> foo3 = new ArrayList(); // Object is a superclass of Integer 
  1. Lectura : dadas las posibles asignaciones anteriores, qué tipo de objeto se garantiza que recibirás cuando leas la List foo3 :

    • No tiene garantizado un Integer porque foo3 podría estar apuntando a una List o a una List .
    • No tiene garantizado un Number porque foo3 podría estar apuntando a una List .
    • La única garantía es que obtendrá una instancia de un Object o subclase de Object (pero no sabe qué subclase).
  2. Escritura : dadas las posibles asignaciones anteriores, ¿qué tipo de objeto podría agregar a la List foo3 que sería legal para todas las posibles asignaciones de ArrayList anteriores?

    • Puede agregar un Integer porque se permite un Integer en cualquiera de las listas anteriores.
    • Puede agregar una instancia de una subclase de Integer porque se permite una instancia de una subclase de Integer en cualquiera de las listas anteriores.
    • No puede agregar un Double porque foo3 podría estar apuntando a un ArrayList .
    • No puede agregar un Number porque foo3 podría estar apuntando a una ArrayList .
    • No puede agregar un Object porque foo3 podría estar apuntando a una ArrayList .

PECS

Recuerde PECS : “Producer Extends, Consumer Super” .

  • “Producer Extends” – Si necesita una List para producir valores T (¿desea leer T s de la lista), necesita declararlo ? extends T ? extends T , por ejemplo, List< ? extends Integer> List< ? extends Integer> . Pero no puedes agregar a esta lista.

  • “Consumer Super” : si necesita una List para consumir valores T (¿desea escribir T s en la lista), necesita declararla ? super T ? super T , por ejemplo, List< ? super Integer> List< ? super Integer> . Pero no hay garantías de qué tipo de objeto puede leer de esta lista.

  • Si necesita leer y escribir en una lista, debe declararlo exactamente sin comodines, por ejemplo, List .

Ejemplo

Tenga en cuenta este ejemplo de las preguntas frecuentes de Java Generics . Observe cómo la lista de fonts src (la lista de productores) se extends y la lista de destinos dest (la lista de consumidores) usa super :

 public class Collections { public static  void copy(List< ? super T> dest, List< ? extends T> src) { for (int i = 0; i < src.size(); i++) dest.set(i, src.get(i)); } } 

También vea ¿Cómo puedo agregar a List < ? se extiende Número> estructuras de datos?

Imagina tener esta jerarquía

enter image description here

1. Extiende

Al escribir

  List< ? extends C2> list; 

usted está diciendo que la list podrá hacer referencia a un objeto de tipo (por ejemplo) ArrayList cuyo tipo genérico es uno de los 7 subtipos de C2 ( C2 incluido):

  1. C2: new ArrayList(); , (un objeto que puede almacenar C2 o subtipos) o
  2. D1: new ArrayList(); , (un objeto que puede almacenar D1 o subtipos) o
  3. D2: new ArrayList(); , (un objeto que puede almacenar D2 o subtipos) o …

y así. Siete casos diferentes:

  1) new ArrayList(): can store C2 D1 D2 E1 E2 E3 E4 2) new ArrayList(): can store D1 E1 E2 3) new ArrayList(): can store D2 E3 E4 4) new ArrayList(): can store E1 5) new ArrayList(): can store E2 6) new ArrayList(): can store E3 7) new ArrayList(): can store E4 

Tenemos un conjunto de tipos “almacenables” para cada caso posible: 7 conjuntos (rojos) aquí representados gráficamente

enter image description here

Como puede ver, no hay un tipo seguro que sea común en todos los casos:

  • no se puede list.add(new C2(){}); porque podría ser list = new ArrayList();
  • no se puede list.add(new D1(){}); porque podría ser list = new ArrayList();

y así.

2. Súper

Al escribir

  List< ? super C2> list; 

usted está diciendo que la list podrá hacer referencia a un objeto de tipo (por ejemplo) ArrayList cuyo tipo genérico es uno de los 7 supertipos de C2 ( C2 incluido):

  • A1: new ArrayList(); , (un objeto que puede almacenar A1 o subtipos) o
  • A2: new ArrayList(); , (un objeto que puede almacenar A2 o subtipos) o
  • A3: new ArrayList(); , (un objeto que puede almacenar A3 o subtipos) o …

y así. Siete casos diferentes:

  1) new ArrayList(): can store A1 B1 B2 C1 C2 D1 D2 E1 E2 E3 E4 2) new ArrayList(): can store A2 B2 C1 C2 D1 D2 E1 E2 E3 E4 3) new ArrayList(): can store A3 B3 C2 C3 D1 D2 E1 E2 E3 E4 4) new ArrayList(): can store A4 B3 B4 C2 C3 D1 D2 E1 E2 E3 E4 5) new ArrayList(): can store B2 C1 C2 D1 D2 E1 E2 E3 E4 6) new ArrayList(): can store B3 C2 C3 D1 D2 E1 E2 E3 E4 7) new ArrayList(): can store C2 D1 D2 E1 E2 E3 E4 

Tenemos un conjunto de tipos “almacenables” para cada caso posible: 7 conjuntos (rojos) aquí representados gráficamente

enter image description here

Como puede ver, aquí tenemos siete tipos de seguridad que son comunes a todos los casos: C2 , D1 , D2 , E1 , E2 , E3 , E4 .

  • puedes list.add(new C2(){}); porque, independientemente del tipo de lista a la que nos estamos refiriendo, se permite C2
  • puedes list.add(new D1(){}); porque, independientemente del tipo de lista a la que nos estamos refiriendo, se permite D1

y así. Probablemente haya notado que estos tipos corresponden a la jerarquía a partir del tipo C2 .

Notas

Aquí la jerarquía completa si deseas hacer algunas pruebas

 interface A1{} interface A2{} interface A3{} interface A4{} interface B1 extends A1{} interface B2 extends A1,A2{} interface B3 extends A3,A4{} interface B4 extends A4{} interface C1 extends B2{} interface C2 extends B2,B3{} interface C3 extends B3{} interface D1 extends C1,C2{} interface D2 extends C2{} interface E1 extends D1{} interface E2 extends D1{} interface E3 extends D2{} interface E4 extends D2{} 

Me encanta la respuesta de @Bert F, pero esta es la forma en que mi cerebro lo ve.

Tengo una X en mi mano. Si quiero escribir mi X en una lista, esa lista debe ser una lista de X o una lista de cosas a las que mi X puede ser copiada mientras las escribo, es decir, cualquier superclase de X …

 List< ? super X> 

Si obtengo una Lista y quiero leer una X de esa Lista, mejor será una Lista de X o una Lista de cosas que pueden ser actualizadas a X cuando las leí, es decir, cualquier cosa que extienda X

 List< ? extends X> 

Espero que esto ayude.

Basado en la respuesta de Bert F, me gustaría explicar mi comprensión.

Digamos que tenemos 3 clases como

 public class Fruit{} public class Melon extends Fruit{} public class WaterMelon extends Melon{} 

Aquí tenemos

 List< ? extends Fruit> fruitExtendedList = … //Says that I can be a list of any object as long as this object extends Fruit. 

Bien, intentemos obtener algún valor de fruitExtendedList

 Fruit fruit = fruitExtendedList.get(position) //This is valid as it can only return Fruit or its subclass. 

Nuevamente intentemos

 Melon melon = fruitExtendedList.get(position) //This is not valid because fruitExtendedList can be a list of Fruit only, it may not be //list of Melon or WaterMelon and in java we cannot assign sub class object to //super class object reference without explicitly casting it. 

Lo mismo es el caso de

 WaterMelon waterMelon = fruitExtendedList.get(position) 

Ahora intentemos establecer algún objeto en fruitExtendedList

Agregar un objeto de fruta

 fruitExtendedList.add(new Fruit()) //This in not valid because as we know fruitExtendedList can be a list of any //object as long as this object extends Fruit. So what if it was the list of //WaterMelon or Melon you cannot add Fruit to the list of WaterMelon or Melon. 

Agregar el objeto Melon

 fruitExtendedList.add(new Melon()) //This would be valid if fruitExtendedList was the list of Fruit but it may //not be, as it can also be the list of WaterMelon object. So, we see an invalid //condition already. 

Finalmente, intente agregar el objeto WaterMelon

 fruitExtendedList.add(new WaterMelon()) //Ok, we got it now we can finally write to fruitExtendedList as WaterMelon //can be added to the list of Fruit or Melon as any superclass reference can point //to its subclass object. 

Pero espere, ¿y si alguien decide hacer un nuevo tipo de limón, digamos por los argumentos Sake SaltyLemon como

 public class SaltyLemon extends Lemon{} 

Ahora fruitExtendedList puede ser una lista de Fruit, Melon, WaterMelon o SaltyLemon.

Entonces, nuestra statement

 fruitExtendedList.add(new WaterMelon()) 

tampoco es válido

Básicamente podemos decir que no podemos escribir nada en una fruitExtendedList.

Esto resume List< ? extends Fruit> List< ? extends Fruit>

Ahora veamos

 List< ? super Melon> melonSuperList= … //Says that I can be a list of anything as long as its object has super class of Melon. 

Ahora intentemos obtener algún valor de melonSuperList

 Fruit fruit = melonSuperList.get(position) //This is not valid as melonSuperList can be a list of Object as in java all //the object extends from Object class. So, Object can be super class of Melon and //melonSuperList can be a list of Object type 

Del mismo modo, Melon, WaterMelon o cualquier otro objeto no se puede leer.

Pero tenga en cuenta que podemos leer instancias de tipo de objeto

 Object myObject = melonSuperList.get(position) //This is valid because Object cannot have any super class and above statement //can return only Fruit, Melon, WaterMelon or Object they all can be referenced by //Object type reference. 

Ahora, intentemos establecer algún valor desde melonSuperList.

Agregar objeto de tipo Objeto

 melonSuperList.add(new Object()) //This is not valid as melonSuperList can be a list of Fruit or Melon. //Note that Melon itself can be considered as super class of Melon. 

Agregar objeto tipo Fruta

 melonSuperList.add(new Fruit()) //This is also not valid as melonSuperList can be list of Melon 

Agregar un objeto tipo Melon

 melonSuperList.add(new Melon()) //This is valid because melonSuperList can be list of Object, Fruit or Melon and in //this entire list we can add Melon type object. 

Agregar objeto tipo WaterMelon

 melonSuperList.add(new WaterMelon()) //This is also valid because of same reason as adding Melon 

Para resumir, podemos agregar Melon o su subclase en melonSuperList y leer solo el objeto tipo de objeto.

super es un límite inferior, y se extiende es un límite superior.

De acuerdo con http://download.oracle.com/javase/tutorial/extra/generics/morefun.html :

La solución es utilizar una forma de comodín delimitado que aún no hemos visto: comodines con un límite inferior. La syntax? super T denota un tipo desconocido que es un supertipo de T (o T en sí, recuerda que la relación de supertipo es reflexiva). Es el doble de los comodines delimitados que hemos estado usando, ¿dónde los usamos? extiende T para denotar un tipo desconocido que es un subtipo de T.

Lo más confuso aquí es que independientemente de las restricciones de tipo que especifiquemos, la asignación solo funciona de una manera:

 baseClassInstance = derivedClassInstance; 

Usted puede pensar que Integer extends Number y que un Integer haría como un < ? extends Number> < ? extends Number> , pero el comstackdor le dirá que < ? extends Number> cannot be converted to Integer < ? extends Number> cannot be converted to Integer (es decir, en el lenguaje humano, es incorrecto que todo lo que se extienda en número se pueda convertir a Integer ):

 class Holder { T v; T get() { return v; } void set(T n) { v=n; } } class A { public static void main(String[]args) { Holder< ? extends Number> he = new Holder(); Holder< ? super Number> hs = new Holder(); Integer i; Number n; Object o; // Producer Super: always gives an error except // when consumer expects just Object i = hs.get(); // < ? super Number> cannot be converted to Integer n = hs.get(); // < ? super Number> cannot be converted to Number // < ? super Number> cannot be converted to ... (but // there is no class between Number and Object) o = hs.get(); // Consumer Super hs.set(i); hs.set(n); hs.set(o); // Object cannot be converted to < ? super Number> // Producer Extends i = he.get(); // < ? extends Number> cannot be converted to Integer n = he.get(); o = he.get(); // Consumer Extends: always gives an error he.set(i); // Integer cannot be converted to < ? extends Number> he.set(n); // Number cannot be converted to < ? extends Number> he.set(o); // Object cannot be converted to < ? extends Number> } } 

hs.set(i); está bien porque Integer se puede convertir a cualquier superclase de Number (y no porque Integer es una superclase de Number , que no es verdadero).

EDIT agregó un comentario sobre Consumer Extends y Producer Super: no son significativos porque especifican, en consecuencia, nada y solo Object . Se recomienda recordar PECS porque CEPS nunca es útil.

El uso de extends solo se puede obtener de la colección. No puedes ponerlo. Además, aunque super permite obtener y poner, ¿el tipo de devolución durante get es ? super T.

Los comodines generics se dirigen a dos necesidades principales:

Lectura de una colección genérica Inserción en una colección genérica Hay tres formas de definir una colección (variable) usando comodines generics. Estos son:

 List< ?> listUknown = new ArrayList(); List< ? extends A> listUknown = new ArrayList(); List< ? super A> listUknown = new ArrayList(); 

List< ?> Significa una lista escrita a un tipo desconocido. Esto podría ser una List , una List , una List etc.

List< ? extends A> List< ? extends A> significa una Lista de objetos que son instancias de la class A , o subclasses of A (por ejemplo, B y C). List< ? super A> List< ? super A> significa que la lista se escribe en la A class o en la superclass of A

Leer más: http://tutorials.jenkov.com/java-generics/wildcards.html

Cuándo usar extends y super

Los comodines son más útiles en los parámetros del método. Permiten la flexibilidad necesaria en las interfaces de métodos.

Las personas a menudo se confunden sobre cuándo usar extensiones y cuándo usar límites súper. La regla de oro es el principio de obtener. Si obtiene algo de un contenedor parametrizado, use extends.

 int totalFuel(List< ? extends Vehicle> list) { int total = 0; for(Vehicle v : list) { total += v.getFuel(); } return total;} 

El método totalFuel obtiene Vehículos de la lista, les pregunta sobre cuánto combustible tienen y calcula el total. Si coloca objetos en un contenedor con parámetros, use super.

 int totalValue(Valuer< ? super Vehicle> valuer) { int total = 0; for(Vehicle v : vehicles) { total += valuer.evaluate(v); } return total;} 

El método totalValue coloca Vehículos en el Valuador. Es útil saber que extiende límite es mucho más común que super.

Puede ver todas las respuestas anteriores para comprender por qué el .add() está restringido a '< ?>' , '< ? extends>' '< ? extends>' , y en parte a '< ? super>' '< ? super>' .

Pero aquí está la conclusión de todo esto si desea recordarlo, y no quiere ir a explorar la respuesta cada vez:

List< ? extends A> List< ? extends A> significa que aceptará cualquier List de A y subclase de A Pero no puedes agregar nada a esta lista. Ni siquiera objetos de tipo A

List< ? super A> List< ? super A> significa que esto aceptará cualquier lista de A y superclase de A Puede agregar objetos de tipo A y sus subclases.

Lista < ? extiende X> no permite agregar nada en la lista.

Lista < ? super X> permite agregar cualquier cosa que sea-a X (X o su subclase).

Las respuestas arriba votadas cubren los detalles de muchos aspectos. Sin embargo, trataría de responder a esto de otra manera.

Hay dos cosas que debemos tener en cuenta,

1. Asignación a la variable de lista

List< ? extends X> listvar;

Aquí, cualquier lista de X o lista de subclases de X se puede asignar a listvar.

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


List< ? super X> listvar;

Aquí, cualquier lista de X o lista de superclases de X se puede asignar a listvar.

List< ? super Number> listvar; listvar = new ArrayList(); listvar = new ArrayList();

2. Realice la operación de lectura o escritura en la variable de lista

 `List< ? extends X> listvar;` 

Puede usar esta función para aceptar una lista en los argumentos del método y realizar cualquier operación en el tipo X (Nota: solo puede leer objetos de tipo X de la lista).

 `List< ? super Number> listvar; 

Puede usar esta función para aceptar una lista en los argumentos del método y realizar cualquier operación en el tipo Objeto, ya que solo puede leer objetos de tipo Objeto de la lista. Pero sí, algo adicional es que puede agregar objetos de tipo X a la lista.

  | v 

Lista < ? extiende X> ? puede ser cualquier subclase de X o ella misma X

Lista < ? super X> ? puede ser cualquier superclase de X o X misma.

  ^ | 

Ejemplo, el orden de herencia se asume como O> S> T> U> V

Usando extends Keyword,

Correcto:

 List< ? extends T> Object = new List(); List< ? extends T> Object = new List(); List< ? extends T> Object = new List(); 

Incorrecto:

 List< ? extends T> Object = new List(); List< ? extends T> Object = new List(); 

super Palabra clave:

Correcto:

 List< ? super T> Object = new List(); List< ? super T> Object = new List(); List< ? super T> Object = new List(); 

Incorrecto:

 List< ? super T> Object = new List(); List< ? super T> Object = new List(); 

Agregar objeto: List Object = new List ();

 Object.add(new T()); //error 

Pero, ¿por qué error? Veamos las posibilidades de inicializaciones de List Object

 List< ? extends T> Object = new List(); List< ? extends T> Object = new List(); List< ? extends T> Object = new List(); 

Si usamos Object.add (nuevo T ()); entonces será correcto solo si

 List< ? extends T> Object = new List(); 

Pero hay dos posibilidades adicionales

List Object = new List (); List Object = new List (); Si tratamos de agregar (nuevo T ()) a las dos posibilidades anteriores, dará un error porque T es la clase superior de U y V. tratamos de agregar un objeto T [que es (nuevo T ())] a la Lista de tipo U y V. El objeto de clase superior (clase Base) no se puede pasar al objeto de clase inferior (Subclase).

Debido a las dos posibilidades adicionales, Java le proporciona un error incluso si utiliza la posibilidad correcta ya que Java no sabe a qué objeto se refiere. Por lo tanto, no puede agregar objetos a List Object = new List (); ya que hay posibilidades que no son válidas.

Agregar objeto: List Object = new List ();

 Object.add(new T()); // compiles fine without error Object.add(new U()); // compiles fine without error Object.add(new V()); // compiles fine without error Object.add(new S()); // error Object.add(new O()); // error 

¿Pero por qué ocurre el error en los dos anteriores? podemos usar Object.add (new T ()); solo en las siguientes posibilidades

 List< ? super T> Object = new List(); List< ? super T> Object = new List(); List< ? super T> Object = new List(); 

Si intentamos usar Object.add (new T ()) en List Object = new List (); y List Object = new List (); entonces dará error Esto se debe a que no podemos agregar el objeto T [que es nuevo T ()] a la Lista Objeto = nueva Lista (); porque es un objeto de tipo U No podemos agregar un objeto T [que es nuevo T ()] a U objeto porque T es una clase base y U es una subclase. No podemos agregar la clase base a la subclase y es por eso que ocurre el error. Esto es lo mismo para el otro caso.