2010-05-02 33 views

Respuesta

15

Asumamos por un momento que podría hacer lo que usted describe:

class B extends A { ... } 

Collection<A> collecA; 
List<B> listB; 

collecA = listB; // normally an error, but lets pretend its allowed 

collecA.add(new A()); // PROBLEM! 

La llamada al método collecA.add(new A()) parece bien ya collecA es una colección que contiene A s. Sin embargo, si la asignación anterior fue permitida, entonces tenemos un problema porque collecA es realmente referencia a una instancia List<B> - Acabo de agregué un A en una lista que solo puede contener B s!

Asker también dijo:

No puedo entender por qué ya Collection es implementado por Lista.

No importa que la colección es una superclase de la lista. Esta asignación es ilegal incluso si usó dos listas.

class B extends A { ... } 
List<A> listA; 
List<B> listB; 
listA = listB; // still an error, still leads to the same problem 

La clave es que el List<A> variables sólo pueden hacer referencia List s que pueden contener A s. Sin embargo, una instancia List<B> no puede contener A s. Por lo tanto, un List<A>variable de como listA no se puede asignar una referencia a una instancia deList<B>mencionado por listB.

o más generalmente hablando: B ser una subclase de A hace no implica que SomeGenericClass<B> es una subclase de SomeGenericClass<A> (JLS §4.10: Subtipificación no se extiende a través de tipos genéricos: T <: U no implica que C<T> <: C<U>.)


fue este ejemplo/analogía de la Java Generics Tutorial que me ayudó a entender esto:

http://java.sun.com/docs/books/tutorial/java/generics/subtyping.html

"Entender por qué se hace mucho más fácil si se piensa en objetos tangibles - las cosas en realidad se puede imagen - como una jaula:

// A cage is a collection of things, with bars to keep them in. 
interface Cage<E> extends Collection<E>; 
... 
Cage<Lion> lionCage = ...; 
Cage<Butterfly> butterflyCage = ...; 

Pero, ¿y una "jaula de animales"? Inglés es ambigua, por lo que para ser precisos vamos a suponer que estamos hablando de una "jaula de todos los animales" :

Cage<Animal> animalCage = ...; 

Esta es una jaula diseñada para contener todo tipo de animales, mezclados entre sí. Debe tener barras lo suficientemente fuertes como para sostenerse en los leones, y espaciadas lo suficientemente cerca como para contener a las mariposas.
...
Dado que un león es un tipo de animal (León es un subtipo de Animal), la pregunta es: "¿Es una jaula de león una especie de jaula de animal? ¿Es Cage<Lion> un subtipo de Cage<Animal>?". Según la definición anterior de jaula animal, la respuesta debe ser "no". ¡Esto es sorprendente! Pero tiene mucho sentido cuando lo piensas: no se puede asumir que una jaula de león se queda en mariposas, y no se puede asumir que una jaula de mariposa tiene leones. Por lo tanto, ni jaula puede ser considerada como una jaula "para todos los animales":..

animalCage = lionCage; // compile-time error 
animalCage = butterflyCage; // compile-time error 

"

+3

buenas analogías tontas fácil de entender la explicación Bert Siempre he apreciado cuando se intenta comprender los genéricos y la herencia debe Kartoch mira esto. – tgai

+2

@Tamon - Me alegro de que (y afortunadamente otros) haya sido fácil de entender. Tuve el mismo problema para comprender los genéricos y la herencia (sin mencionar los comodines genéricos), así que tomé un ejemplo nuevo como el Tutorial de genéricos de Java para inculcarlo en mi cabeza. Hice una búsqueda de 'Java genérico molde de jaula de animal' para recordar exactamente dónde leo esta analogía. :-) –

+0

Muy buena explicación, de hecho ... – Kartoch

5

Los genéricos de Java no son covariant.

Ver Java Theory and Practice: Generics Gotchas para más detalles.

La página muestra un ejemplo sencillo que sería el caos del tipo del sistema si era covariante:

Imagínese que usted podría asignar un número entero Lista <> a un número de lista <>. A continuación, el siguiente código permitirá poner algo que no era un entero en una lista < Integer>:

List<Integer> li = new ArrayList<Integer>(); 
List<Number> ln = li; // illegal 
ln.add(new Float(3.1415)); // ERROR: Adds a float to li, which is a list of Integers! 
8
Collection<? extends A> collecA 

Esto lo arregla. El problema no es el List extends Collection, sino los tipos genéricos en su lugar.

1

Puede asignar la Lista < B> a la Colección < B>, pero no la Lista < B> a la Colección < A>.

Imagínese lo que pasaría si esto fuera posible:

List<B> = new ArrayList<B>(); 
Collection<A> collecA = listB; //Assume that this line compiles 
collecA.add(new A()); 
B item = listB.get(0); //ClassCastException! 

Como se ve, que "engañar" el sistema de tipos genéricos, mediante la adición de una instancia del tipo concreto de A a una colección que se suponía que tienen solamente objetos de tipo B (o descendientes). Como consecuencia, la última línea que realiza un lanzamiento implícito a B falla con una ClassCastException. ¿Qué pasa con eso? El compilador no puede garantizar la seguridad del tipo, y eso va en contra de uno de los principios genéricos de Java.

Por lo tanto, se ha decidido que < Lista B> es Colección < B>, pero no enumera < A> (o colección < A>).

Como comentario adicional, es interesante observar que las matrices no siguen las mismas reglas: String [] es un Object [], y las asignaciones son legales.

Cuestiones relacionadas