java - quien - Genéricos: Lista<? extiende Animal> es igual que Lista<Animal>?
juegos con arboles binarios (3)
Solo intento comprender la palabra clave extends
en Java Generics.
List<? extends Animal>
List<? extends Animal>
significa que podemos rellenar cualquier objeto en la List
que ES UN Animal
entonces, lo siguiente no significará lo mismo:
List<Animal>
¿Alguien puede ayudarme a saber la diferencia entre los dos anteriores? Para mí se extends
solo sonido redundante aquí.
¡Gracias!
No lo es. List<Animal>
dice que el valor asignado a esta variable debe ser de "tipo" List<Animal>
. Sin embargo, esto no significa que solo debe haber objetos de Animal
, también puede haber subclases.
List<Number> l = new ArrayList<Number>();
l.add(4); // autoboxing to Integer
l.add(6.7); // autoboxing to Double
Usas la List<? extends Number>
List<? extends Number>
construir si le interesa una lista que tiene objetos Number
, pero el objeto List no necesita ser del tipo List<Number>
pero puede incluir cualquier otra lista de subclases (como List<Integer>
).
En algún momento se usan los argumentos del método para decir "Quiero una lista de Numbers
, pero no me importa si es solo List<Number>
, también puede ser una List<Double>
" . Esto evita algunos lanzamientos extraños si tiene una lista de algunas subclases, pero el método espera una lista de la clase base.
public void doSomethingWith(List<Number> l) {
...
}
List<Double> d = new ArrayList<Double>();
doSomethingWith(d); // not working
Esto no funciona como esperaba List<Number>
, no List<Double>
. Pero si escribiste List<? extends Number>
List<? extends Number>
puede pasar los objetos de la List<Double>
incluso si no son objetos de la List<Number>
.
public void doSomethingWith(List<? extends Number> l) {
...
}
List<Double> d = new ArrayList<Double>();
doSomethingWith(d); // works
Nota: Todo esto no está relacionado con la herencia de los objetos en la lista en sí. Todavía puede agregar objetos Double
e Integer
en una List<Number>
, con o sin ? extends
? extends
cosas
Veo que ya has aceptado una respuesta, pero me gustaría añadir mi opinión, ya que creo que puedo ser de ayuda aquí.
La diferencia entre List<Animal>
y List<? extends Animal>
List<? extends Animal>
es el siguiente.
Con List<Animal>
, sabes que lo que tienes es definitivamente una lista de animales. No es necesario que todos ellos sean exactamente ''Animales''; también podrían ser tipos derivados. Por ejemplo, si tiene una Lista de animales, tiene sentido que una pareja pueda ser Cabra, y algunos de ellos Gatos, etc. ¿Verdad?
Por ejemplo, esto es totalmente válido:
List<Animal> aL= new List<Animal>();
aL.add(new Goat());
aL.add(new Cat());
Animal a = aL.peek();
a.walk(); // assuming walk is a method within Animal
Por supuesto, lo siguiente no sería válido:
aL.peek().meow(); // we can''t do this, as it''s not guaranteed that aL.peek() will be a Cat
Con List<? extends Animal>
List<? extends Animal>
, estás haciendo una declaración sobre el tipo de lista con la que estás tratando.
Por ejemplo:
List<? extends Animal> L;
Esto en realidad no es una declaración del tipo de objeto que L puede contener . Es una declaración sobre los tipos de listas a las que L puede hacer referencia.
Por ejemplo, podríamos hacer esto:
L = aL; // remember aL is a List of Animals
Pero ahora todo lo que el compilador sabe acerca de L es que es una Lista de [ya sea Animal o un subtipo de Animal] s
Entonces, lo siguiente no es válido:
L.add(new Animal()); // throws a compiletime error
Porque, por lo que sabemos, L podría estar haciendo referencia a una lista de Cabras, a la que no podemos agregar un Animal.
Este es el por qué:
List<Goat> gL = new List<Goat>(); // fine
gL.add(new Goat()); // fine
gL.add(new Animal()); // compiletime error
En lo anterior, estamos intentando lanzar un Animal como Cabra. Eso no funciona, porque ¿qué pasa si después de hacer eso tratamos de hacer que Animal haga un "cabezazo", como lo haría una cabra? No necesariamente sabemos que el Animal puede hacer eso.
List<Dog>
es un subtipo de List<? extends Animal>
List<? extends Animal>
, pero no un subtipo de List<Animal>
.
¿Por qué List<Dog>
no es un subtipo de List<Animal>
? Considere el siguiente ejemplo:
void mySub(List<Animal> myList) {
myList.add(new Cat());
}
Si se le permitiera pasar una List<Dog>
a esta función, obtendría un error de tiempo de ejecución.
EDITAR: Ahora, si usamos List<? extends Animal>
List<? extends Animal>
lugar, sucederá lo siguiente:
void mySub(List<? extends Animal> myList) {
myList.add(new Cat()); // compile error here
Animal a = myList.get(0); // works fine
}
Podría pasar una List<Dog>
a esta función, pero el compilador se da cuenta de que agregar algo a la lista podría ocasionarle problemas. Si usa super
lugar de extends
(lo que le permite pasar una List<LifeForm>
), es al revés.
void mySub(List<? super Animal> myList) {
myList.add(new Cat()); // works fine
Animal a = myList.get(0); // compile error here, since the list entry could be a Plant
}
La teoría detrás de esto es Co- y Contravariancia .