Tengo un par de preguntas sobre comodines genéricos en Java:
¿Cuál es la diferencia entre
List<? extends T>
yList<? super T>
?¿Qué es un comodín acotado y qué es un comodín ilimitado?
Tengo un par de preguntas sobre comodines genéricos en Java:
¿Cuál es la diferencia entre List<? extends T>
y List<? super T>
?
¿Qué es un comodín acotado y qué es un comodín ilimitado?
Respuestas:
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 .
<? super C>
significaría que su tipo está restringido a algo arriba C
en la jerarquía de tipos. (Perdón por la respuesta extremadamente tardía. Supongo que no tuvimos notificaciones de comentarios hace 2 años)
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 B
donde 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.
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?
Josh Bloch también tiene una buena explicación de cuándo usar super
y extends
en esta charla en video de google io donde menciona el mnemónico Producer extends
Consumersuper
.
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
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).
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));
}
}
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:
List<A>
, entonces se nos debería permitir dar List<A-sub>
a este método. (Porque A-sub ES una A)List<A>
, entonces se nos debería permitir ceder List<A-super>
a este método. (Porque A ES un A-super)