respuestas quien programa preguntas juegos juego hacer crear con como binarios arboles animales akinator adivinador adivina java generics covariance extends contravariance

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 .