type - Java Generics(comodines)
java wildcard capture (7)
Tengo un par de preguntas sobre comodines genéricos en Java:
¿Cuál es la diferencia entre
List<? extends T>
List<? extends T>
yList<? super T>
List<? super T>
?¿Qué es un comodín delimitado y qué es un comodín ilimitado?
En general,
Si una estructura contiene elementos con un tipo de la forma
? extends E
? extends E
, podemos obtener 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));
}
}
En tu primera pregunta, <? extends T>
<? extends T>
y <? super T>
<? super T>
son ejemplos de comodines delimitados. Un comodín ilimitado se parece a <?>
, Y básicamente significa <? extends Object>
<? extends Object>
. Significa vagamente que el genérico puede ser de cualquier tipo. Un comodín delimitado ( <? extends T>
o <? super T>
) coloca una restricción en el tipo diciendo que o bien tiene que extender un tipo específico ( <? extends T>
se conoce como un límite superior), o tiene que ser un ancestro de un tipo específico ( <? super T>
se conoce como un límite inferior).
Los Tutoriales de Java tienen algunas explicaciones bastante buenas de genéricos en los artículos Wildcards y Más Diversión con Comodines .
Josh Bloch también tiene una buena explicación de cuándo usar super
y se extends
en esta charla de video de google io, donde menciona que el Productor extends
Consumer super
mnemonic.
De las diapositivas de la presentación:
Supongamos 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 E
Los comodines genéricos se crean para que los métodos que operan en Colección sean más reutilizables.
Por ejemplo, si un método tiene un parámetro List<A>
, solo podemos proporcionar List<A>
a este método. Es un desperdicio para la función de este método en algunas circunstancias:
- Si este método solo lee objetos de
List<A>
, entonces debería permitírsele darList<A-sub>
a este método. (Debido a que A-sub IS a A) - Si este método solo inserta objetos en la
List<A>
, entonces debería permitírsele asignarList<A-super>
a este método. (Porque A ES un A-super)
Por ejemplo, tenemos la siguiente jerarquía de clases
Objeto <- A <- B, C
Deberías usar List <? extends A>
List <? extends A>
(límite superior) si vas a leer de la lista
Cuando sepa que las instancias en la colección son de instancias de A o subclases de A, es seguro leer las instancias de la colección y enviarlas a las instancias A.
No puede insertar elementos en la lista, porque no sabe si la lista está escrita en la clase A, B o C.
Deberías usar List <? super A>
List <? super A>
(límite inferior) si vas a insertar en la lista
Cuando sepa que la lista está escrita en A, o en una superclase de A, es seguro insertar instancias de A o subclases de A (por ejemplo, B o C) en la lista.
Sin embargo, no puede leer de la lista, excepto si arroja los objetos leídos a Object. Los elementos ya presentes en la lista podrían ser de cualquier tipo que sea una A o una superclase de A, pero no es posible saber exactamente qué clase es.
Lea más aquí - http://tutorials.jenkov.com/java-generics/wildcards.html
Puede haber ocasiones en las que desee restringir los tipos de tipos que pueden pasar a un parámetro de tipo. Por ejemplo, un método que opera con números solo podría querer aceptar instancias de Number o sus subclases. Esto es para lo que son los parámetros de tipo delimitados.
Collection<? extends MyObject>
significa que puede aceptar todos los objetos que tengan 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{}
Entonces,
Collection<? extends MyObject> myObject;
aceptará solo MyObject o hijos de MyObject (es decir, cualquier objeto de tipo OurObject o YourObject o MyObject, pero no cualquier objeto de la superclase de MyObject).
Si tiene una jerarquía de clases A, B es una subclase de A, y C y D ambas son subclase de B como a continuación
class A {}
class B extends A {}
class C extends B {}
class D extends B {}
Entonces
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 delimitado es como ? extends B
? extends B
donde B es de algún tipo. Es decir, el tipo es desconocido pero se puede colocar un "límite" sobre él. En este caso, está delimitado por alguna clase, que es una subclase de B.