2011-12-23 93 views
7

我無法理解爲什麼以下不起作用,我相信答案與基本的東西有關,我不理解並希望有人可以提供幫助。ArrayList和接口繼承的泛型類型的問題

我瞭解使用接口在ArrayList這樣的,如果我有:

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

然後你可以插入任何實現武器到武器的數組:

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

我得到的,但是讓我困惑的是理解爲什麼ArrayList<Gun>與其他方式不兼容ArrayList<Weapon>。爲了說明,以下是合法的:

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

但下面的不是:

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

,因爲過去的函數調用報告,該方法並不適用於它的參數。

我的問題是爲什麼如果該方法知道它的任務ArrayList與接口作爲泛型類型,爲什麼在這最後一個語句中傳遞一個數組列表的刀爲什麼不行?

+1

我不知道確切的答案,但你可以爲你的第二個例子做的是使用'public void interfaceIsTheArgument(ArrayList <?extends Weapon>)''。那可行。 – fge

回答

13

「修理」的代碼,你需要使用一個通用的約束

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

的關鍵原因是List<Gun>List<Weapon>一個子類。這樣做的原因其實可以通過這個代碼所示:

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! 

通過使用綁定<? extends Weapon>,我們說,我們會接受任何普通類型,它是一個子類的Weapon。使用邊界可以非常強大。這種綁定是upper bound - 我們將頂級類別指定爲Weapon

還有一個約束,使用的語法如下:

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

所以,當使用每一個?記住這個詞PECS:「生產者擴展,超級消費者」。這意味着在代碼的生產者端(其中對象是創建的)使用extends,並且在代碼的消費者端(其中對象是使用)us super。一旦你嘗試了幾次,你就會通過經驗瞭解它爲什麼能夠很好地工作。

This SO question/answer覆蓋得很好。

4

這是關於仿製藥最常問的問題之一。這一個List<Gun>List<Weapon>,你可以做

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

因此,這將打破類型安全的泛型承諾。