2011-12-23 11 views
7

Tengo problemas para entender por qué lo siguiente no funciona y estoy seguro de que la respuesta está relacionada con algo básico que no entiendo y espero que alguien pueda ayudar.Problema con los tipos genéricos de ArrayList y herencia de interfaz

entiendo sobre el uso de las interfaces en un ArrayList tal que si tengo:

public interface Weapon { ... } 
public class Gun implements Weapon { ...} 
public class Knife implements Weapon { ... } 

A continuación, puede insertar cualquier cosa que implementa Arma en el una serie de armas:

ArrayList<Weapon> weapons = new ArrayList<Weapon>(); 
weapons.add(new Gun()); 
weapons.add(new Knife(); 

lo entiendo , pero lo que me confunde es la comprensión de por qué ArrayList<Gun> no es compatible con ArrayList<Weapon> de otras maneras. Para ilustrar esto, la siguiente es legal:

public void interfaceIsTheArgument(Weapon w) { ... } 
... 
interfaceIsTheArgument(new Gun()); 
interfaceIsTheArgument(new Knife()); 

pero los siguientes no es:

public void interfaceIsTheArgument(ArrayList<Weapon> w) { ... } 
... 
interfaceIsTheArgument(new ArrayList<Gun>()); 
interfaceIsTheArgument(new ArrayList<Knife>()); 

debido a los últimos informes de llamadas función que el método no es aplicable a sus argumentos.

Mi pregunta es por qué si el método sabe que tareas una ArrayList con una interfaz como el tipo genérico, ¿por qué no está bien pasar una lista de cuchillos en esa última declaración?

+1

No sé la respuesta exacta, pero lo que puedes hacer para tu segundo ejemplo es usar 'public void interfaceIsTheArgument (ArrayList )'. Que funcionará. – fge

Respuesta

13

para "fijar" el código, es necesario utilizar un genérico obligado:

public void interfaceIsTheArgument(List<? extends Weapon> w) { ... } 
... 
interfaceIsTheArgument(new ArrayList<? extends Weapon>()); 
interfaceIsTheArgument(new ArrayList<Knife>()); 

La razón principal es List<Gun> es no una subclase de List<Weapon>. La razón de este hecho se puede ilustrar con este código:

List<Gun> guns = new ArrayList<Gun>(); 
// If List<Weapon> was a super type of List<Gun>, this next line would be allowed 
List<Weapon> weapons = guns; // Won't compile, but let's assume it did 
weapons.add(new Knife()); // Compiles, because Knife is a Weapon 
Gun gun = guns.get(0); // Oops! guns.get(0) is a Knife, not a Gun! 

Mediante el uso de la cota <? extends Weapon>, estamos diciendo que vamos a aceptar cualquier tipo genérico que es una subclase de Weapon. Usar límites puede ser muy poderoso. Este tipo de límite es un superior obligado - estamos especificando la clase de nivel superior como Weapon.

También hay un menor obligado, que utiliza esta sintaxis:

List<? super Weapon> // accept any type that is a Weapon or higher in the class hierarchy 

Por lo tanto, cuándo utilizar cada uno? Recuerde esta palabra PECS: "Producer extends, consumer super". Esto significa que en el lado del productor del código (donde los objetos son creados) use , y en el lado del consumidor del código (donde los objetos son utilizados) us super. Una vez que lo intentes varias veces, entenderás a través de la experiencia por qué funciona bien.

This SO question/answer lo cubre bien.

4

Esta es una de las preguntas más frecuentes sobre los genéricos.Es un List<Gun> era un List<Weapon>, se podría hacer

List<Gun> gunList = new ArrayList<Gun>(); 
List<Weapon> weaponList = gunList; 
weaponList.add(new Knife()); 
gunList.get(0).fire(); // ClassCastException 

Esto sería por lo tanto romper la seguridad de tipos prometida por los genéricos.