manejo - metodos genericos java
Diferencia entre<? super T> y<? extiende T> en Java (15)
Esta pregunta ya tiene una respuesta aquí:
- ¿Qué es PECS (Producer Extends Consumer Super)? 11 respuestas
¿Cuál es la diferencia entre la List<? super T>
List<? super T>
y List<? extends T>
List<? extends T>
?
Solía usar List<? extends T>
List<? extends T>
, pero no me permite agregarle elementos list.add(e)
, mientras que la List<? super T>
List<? super T>
hace.
extends
La declaración de comodín de la List<? extends Number> foo3
List<? extends Number> foo3
significa que cualquiera de estas son asignaciones legales:
List<? extends Number> foo3 = new ArrayList<Number>(); // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>(); // Double extends Number
Lectura : dadas las posibles asignaciones anteriores, ¿qué tipo de objeto se le garantiza leer de la
List foo3
?- Puede leer un
Number
porque cualquiera de las listas que podrían asignarse afoo3
contiene unNumber
o una subclase deNumber
. - No puedes leer un
Integer
porquefoo3
podría estar apuntando a unaList<Double>
. - No puedes leer un
Double
porquefoo3
podría estar apuntando a unaList<Integer>
.
- Puede leer un
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 anteriores deArrayList
:- No puede agregar un
Integer
porquefoo3
podría estar apuntando a unaList<Double>
. - No puedes agregar un
Double
porquefoo3
podría estar apuntando a unaList<Integer>
. - No puedes agregar un
Number
porquefoo3
podría estar apuntando a unaList<Integer>
.
- No puede agregar un
¿No puedes agregar ningún objeto a la List<? extends T>
List<? extends T>
porque no puede garantizar el tipo de List
que realmente apunta, por lo que no puede garantizar que el objeto esté permitido en esa List
. La única "garantía" es que solo puedes leer de ella y obtendrás una T
o subclase de T
super
Ahora considera la List <? super T>
List <? super T>
.
La declaración de comodín de la List<? super Integer> foo3
List<? super Integer> foo3
significa que cualquiera de estas son tareas legales:
List<? super Integer> foo3 = new ArrayList<Integer>(); // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>(); // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>(); // Object is a superclass of Integer
Lectura : dadas las posibles asignaciones anteriores, ¿qué tipo de objeto se le garantiza recibir cuando lea la
List foo3
?- No se le garantiza un
Integer
porquefoo3
podría estar apuntando a unaList<Number>
oList<Object>
. - No se le garantiza un
Number
porquefoo3
podría estar apuntando a unaList<Object>
. - La única garantía es que obtendrá una instancia de un
Object
o subclase deObject
(pero no sabe qué subclase).
- No se le garantiza un
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 anteriores deArrayList
:- Puede agregar un
Integer
porque se permite unInteger
en cualquiera de las listas anteriores. - Puede agregar una instancia de una subclase de
Integer
porque se permite una instancia de una subclase deInteger
en cualquiera de las listas anteriores. - No puedes agregar un
Double
porquefoo3
podría estar apuntando a unArrayList<Integer>
. - No puede agregar un
Number
porquefoo3
podría estar apuntando a unArrayList<Integer>
. - No puede agregar un
Object
porquefoo3
podría estar apuntando a unArrayList<Integer>
.
- Puede agregar un
PECS
Recuerde PECS : "Producer Extends, Consumer Super" .
"Producer Extiende" : si necesita una
List
para producir valoresT
(desea leerT
s de la lista), ¿debe declararlo con? extends T
? extends T
, por ejemplo,List<? extends Integer>
List<? extends Integer>
. Pero no se puede agregar a esta lista."Super consumidor" : si necesita una
List
para consumir valoresT
(desea escribirT
s en la lista), debe declararlo con? 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<Integer>
.
Ejemplo
Tenga en cuenta este ejemplo de las preguntas frecuentes de Java Generics . Observe cómo la lista de origen src
(la lista de producción) usa la extends
, y la lista de destino dest
(la lista de consumidores) usa super
:
public class Collections {
public static <T> 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 la lista <? Extiende Número> estructuras de datos?
Ejemplo, el orden de herencia se asume como O> S> T> U> V
Utilizando extiende la palabra clave,
Correcto:
List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();
Incorrecto:
List<? extends T> Object = new List<S>();
List<? extends T> Object = new List<O>();
super palabra clave:
Correcto:
List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();
Incorrecto:
List<? super T> Object = new List<U>();
List<? super T> Object = new List<V>();
Agregar objeto: Objeto de lista = nuevo List ();
Object.add(new T()); //error
Pero ¿por qué error? Veamos las posibilidades de inicialización de objetos de lista.
List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();
Si usamos Object.add (new T ()); entonces será correcto solo si
List<? extends T> Object = new List<T>();
Pero hay dos posibilidades extra
Objeto de lista = nueva lista (); Objeto de lista = nueva lista (); Si intentamos agregar (nueva T ()) a las dos posibilidades anteriores, dará un error porque T es la clase superior de U y V. intentamos 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 a objeto de clase inferior (subclase).
Debido a las dos posibilidades adicionales, Java le da error incluso si usa 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 (); Como hay posibilidades que no son válidas.
Agregar objeto: Objeto de lista = nuevo 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é el error se produce en los dos anteriores? podemos usar Object.add (new T ()); sólo en las posibilidades de abajo,
List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();
Si intentamos usar Object.add (new T ()) en List Object = new List (); y objeto de lista = nueva lista (); 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 Object 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 se produce un error. Esto es igual para el otro caso.
Imagina tener esta jerarquía
1. se extiende
Por escrito
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
(incluido C2
):
- C2:
new ArrayList<C2>();
, (un objeto que puede almacenar C2 o subtipos) o - D1:
new ArrayList<D1>();
, (un objeto que puede almacenar D1 o subtipos) o - D2:
new ArrayList<D2>();
, (un objeto que puede almacenar D2 o subtipos) o ...
y así. Siete casos diferentes:
1) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
2) new ArrayList<D1>(): can store D1 E1 E2
3) new ArrayList<D2>(): can store D2 E3 E4
4) new ArrayList<E1>(): can store E1
5) new ArrayList<E2>(): can store E2
6) new ArrayList<E3>(): can store E3
7) new ArrayList<E4>(): can store E4
Tenemos un conjunto de tipos "almacenables" para cada caso posible: 7 (rojo) conjuntos aquí representados gráficamente
Como puede ver, no hay un tipo seguro que sea común para todos los casos:
- no puede
list.add(new C2(){});
porque podría serlist = new ArrayList<D1>();
- no puede
list.add(new D1(){});
porque podría serlist = new ArrayList<D2>();
y así.
2. super
Por escrito
List<? super C2> list;
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
(incluido C2
):
- A1:
new ArrayList<A1>();
, (un objeto que puede almacenar A1 o subtipos) o - A2:
new ArrayList<A2>();
, (un objeto que puede almacenar A2 o subtipos) o - A3:
new ArrayList<A3>();
, (un objeto que puede almacenar A3 o subtipos) o ...
y así. Siete casos diferentes:
1) new ArrayList<A1>(): can store A1 B1 B2 C1 C2 D1 D2 E1 E2 E3 E4
2) new ArrayList<A2>(): can store A2 B2 C1 C2 D1 D2 E1 E2 E3 E4
3) new ArrayList<A3>(): can store A3 B3 C2 C3 D1 D2 E1 E2 E3 E4
4) new ArrayList<A4>(): can store A4 B3 B4 C2 C3 D1 D2 E1 E2 E3 E4
5) new ArrayList<B2>(): can store B2 C1 C2 D1 D2 E1 E2 E3 E4
6) new ArrayList<B3>(): can store B3 C2 C3 D1 D2 E1 E2 E3 E4
7) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
Tenemos un conjunto de tipos "almacenables" para cada caso posible: 7 (rojo) conjuntos aquí representados gráficamente
Como puede ver, aquí tenemos siete tipos seguros que son comunes a cada caso: C2
, D1
, D2
, E1
, E2
, E3
, E4
.
- puedes
list.add(new C2(){});
Porque, independientemente del tipo de lista a la que hacemos referencia,C2
está permitido - puede
list.add(new D1(){});
porque, independientemente del tipo de lista a la que hacemos referencia, se permiteD1
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 desea 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{}
Las respuestas más votadas cubren los detalles sobre muchos aspectos. Sin embargo, trataría de responder esto de manera diferente.
Hay 2 cosas que debemos considerar,
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<Number>(); listvar = new ArrayList<Integer>();
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<Number>(); listvar = new ArrayList<Object>();
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 argumentos de 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 argumentos de método y realizar cualquier operación en el tipo Objeto ya que solo puede leer objetos del tipo Objeto de la lista. Pero sí, lo adicional aquí es que puede agregar objetos de tipo X a la lista.
Lo más confuso aquí es que, independientemente de las restricciones de tipo que especifiquemos, la asignación funciona de una sola manera:
baseClassInstance = derivedClassInstance;
Puedes pensar que Integer extends Number
y que un Integer
lo haría como <? extends Number>
<? extends Number>
, pero el compilador te 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 cualquier cosa que extienda el número se pueda convertir en Integer ):
class Holder<T> {
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 en cualquier superclase de Number
(y no porque Integer
sea una superclase de Number
, lo cual no es verdadero).
EDIT agregó un comentario acerca de Consumer Extends y Producer Super; no tienen sentido porque especifican, correspondientemente, nada y solo Object
. Se recomienda recordar PECS porque CEPS nunca es útil.
Los comodines genéricos apuntan a dos necesidades primarias:
Lectura de una colección genérica Inserción en una colección genérica Hay tres formas de definir una colección (variable) utilizando comodines genéricos. Estos son:
List<?> listUknown = new ArrayList<A>();
List<? extends A> listUknown = new ArrayList<A>();
List<? super A> listUknown = new ArrayList<A>();
List<?>
Significa una lista escrita con un tipo desconocido. Esto podría ser una List<A>
, una List<B>
, una List<String>
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 ya sea en la A class
o en una superclass of A
Lea más: http://tutorials.jenkov.com/java-generics/wildcards.html
Me encanta la respuesta de @Bert F pero esta es la forma en que mi cerebro la 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 transmitida al escribirlas, es decir, cualquier superclase de X ...
List<? super X>
Si obtengo una Lista y quiero leer una X fuera de esa Lista, será mejor una Lista de X o una Lista de cosas que pueden ser enviadas a X cuando las leo, es decir, cualquier cosa que se extienda X
List<? extends X>
Espero que esto ayude.
Me gustaría visualizar la diferencia. Supongamos que tenemos:
class A { }
class B extends A { }
class C extends B { }
List<? extends T>
List<? extends T>
- leyendo y asignando:
|-------------------------|-------------------|---------------------------------|
| wildcard | get | assign |
|-------------------------|-------------------|---------------------------------|
| List<? extends C> | A B C | List<C> |
|-------------------------|-------------------|---------------------------------|
| List<? extends B> | A B | List<B> List<C> |
|-------------------------|-------------------|---------------------------------|
| List<? extends A> | A | List<A> List<B> List<C> |
|-------------------------|-------------------|---------------------------------|
List<? super T>
List<? super T>
- escribiendo y asignando:
|-------------------------|-------------------|--------------------------------------------|
| wildcard | add | assign |
|-------------------------|-------------------|--------------------------------------------|
| List<? super C> | C | List<Object> List<A> List<B> List<C> |
|-------------------------|-------------------|--------------------------------------------|
| List<? super B> | B C | List<Object> List<A> List<B> |
|-------------------------|-------------------|--------------------------------------------|
| List<? super A> | A B C | List<Object> List<A> |
|-------------------------|-------------------|--------------------------------------------|
En todos los casos:
- siempre puede obtener
Object
de una lista independientemente del comodín. - siempre puede agregar un
null
a una lista mutable independientemente del comodín.
Puede revisar todas las respuestas anteriores para comprender por qué .add()
está restringido a ''<?>''
, ''<? extends>''
''<? extends>''
, y en parte a ''<? super>''
''<? super>''
.
Pero aquí está la conclusión de todo esto si quieres recordarlo y no quieres ir a explorar la respuesta todas las veces:
List<? extends A>
List<? extends A>
significa que aceptará cualquier List
de A
y subclase de A
Pero no puedes añadir nada a esta lista. Ni siquiera objetos de tipo A
List<? super A>
List<? super A>
significa que aceptará cualquier lista de A
y superclase de A
Puede agregar objetos de tipo A
y sus subclases.
Sobre la base de 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.
Ok, ahora tratemos de obtener algún valor de fruitExtendedList
Fruit fruit = fruitExtendedList.get(position)
//This is valid as it can only return Fruit or its subclass.
De nuevo vamos a intentar
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.
Igual es el caso para
WaterMelon waterMelon = fruitExtendedList.get(position)
Ahora vamos a tratar de establecer algún objeto en fruitExtendedList
Añadiendo 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.
Añadiendo 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 vamos a intentar 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 razones, Salty Limón como
public class SaltyLemon extends Lemon{}
Ahora fruitExtendedList puede ser una lista de Fruit, Melon, WaterMelon o SaltyLemon.
Por lo tanto, nuestra declaración
fruitExtendedList.add(new WaterMelon())
Tampoco es válido.
Básicamente podemos decir que no podemos escribir nada en una lista extendida de frutas.
Esto resume la 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 algo de 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 pueden leer.
Pero ten en cuenta que podemos leer instancias de tipo 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 de 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.
Agregando objeto tipo fruta
melonSuperList.add(new Fruit())
//This is also not valid as melonSuperList can be list of Melon
Añadiendo objeto tipo melón
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.
Añadiendo objeto de 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 Objeto de tipo Objeto.
Usando extensiones solo puedes obtener de la colección. No puedes poner en ello. Además, aunque super permite tanto obtener y poner, el tipo de retorno durante la obtención es ? super t .
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 acotado que aún no hemos visto: comodines con un límite inferior. La sintaxis? 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 acotados que hemos estado usando, ¿dónde los usamos? extiende T para denotar un tipo desconocido que es un subtipo de T.
Cuándo usar extendido y super
Los comodines son más útiles en los parámetros del método. Permiten la flexibilidad necesaria en las interfaces de método.
La gente a menudo se confunde cuando usar extensiones y cuándo usar súper límites. La regla de oro es el principio get-put. Si obtiene algo de un contenedor parametrizado, su uso se extiende.
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 la cantidad de combustible que tienen y calcula el total. Si coloca objetos en un contenedor parametrizado, 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 pone Vehículos en el Valuer. Es útil saber que el límite extendido es mucho más común que el súper.
Lista <? extiende X> no permite agregar nada, excepto nulo, a la lista.
Lista <? super X> permite agregar cualquier cosa que sea: una X (X o su subclase) o nula.
|
v
Lista <? extiende X> ? Puede ser cualquier subclase de
X
oX
sí.Lista <? super X> ? Puede ser cualquier superclase de
X
oX
misma.
^
|