2011-03-31 39 views
16

比方說,我有這些類:汽車,汽車和飛船:Java泛型通配符問題:列表<? extends A>

class Vehicle{ 

    void rideVehicle(Vehicle v){ 
     System.out.println("I am riding a vehicle!"); 
    } 

} 

class Car extends Vehicle{ 
    void rideVehicle(Vehicle c){ 
     System.out.println("I am riding a car!"); 
    } 
} 


class SpaceShip extends Vehicle{ 
    void rideVehicle(Vehicle c){ 
     System.out.println("I am riding a spaceship!"); 
    } 

} 

,我寫這個方法addCars:

private static void addCars(List<? extends Vehicle> vcls){ 
     vcls.add(new Car()); 
     vcls.add(new Car()); 
     vcls.add(new Car()); 

    } 

爲什麼我得到一個編譯時間錯誤?我知道List是擴展Vehicle的任何X的List的超類型。對?

感謝

編輯:我得到的錯誤(編譯時間):Add方法(?捕獲#車輛2的延伸)在類型列表中是不適用的參數(車)。

+0

可以共享編譯錯誤?乍一看,這看起來應該工作... – 2011-03-31 04:42:35

+0

@Mark Elliot:編輯我的問題,編譯錯誤 – 2011-03-31 04:43:44

回答

20

方法參數是在亞型逆變,並且由通配符的定義,對於每個類型T延伸VehicleFoo<T>Foo<* extends Vehicle>子類型。這意味着當你只關心返回類型時,通配符是很好的,但是當你想要將類型的值傳遞給方法時,在這種情況下不會工作。

的問題是,用戶可能會嘗試撥打

List<SpaceShip> l = ... 
addCars(l); 

如果你的代碼是編譯,l然後將含有3輛汽車飛船的列表。顯然沒有好處。

1

參數類型爲? extends Vehicle,表示Vehicle的未知子類型 。由於我們不知道它是什麼類型,我們不知道它是否是Car的超類型 ;它可能會或可能不是這樣的超類型,所以在那裏通過一個 Car是不安全的。

閱讀this tutorial的第7頁。

1

當你說<? extends Vehicle>這意味着它可以是任何類型的車輛。這意味着有人可以通過列表,它會接受它。現在List<Spaceship>不能將新Car()作爲他的一個項目。所以爲了避免這些錯誤,如果您使用了通配符表達式,則不允許在列表中添加任何對象。

4
private static void addCars(List<? extends Vehicle> vcls){ 

應該

private static void addCars(List<? super Vehicle> vcls){ 

這將解決編譯時錯誤。編輯:閱讀here

+0

謝謝我知道,但我很困惑,爲什麼第一個不工作 – 2011-03-31 04:58:09

+0

@Saher:'?延伸「意味着只得到。唯一的例外是你可以設置null。 – 2011-03-31 05:01:11

1

既可以使用:

private static void addCars(List<? super Vehicle> vcls) 

(這意味着調用者傳遞是車輛或超類型對象的列表)

private static void addCars(List<Vehicle> vcls) 
7

Here's一個指向你爲什麼會遇到編譯錯誤的指針。具體而言,

列表是一個有界通配符的示例 。這個?代表 未知類型,就像我們前面看到的 通配符。但是,在 這種情況下,我們知道這個未知的 類型實際上是Shape的一個子類型。 (注意:它可以是Shape本身,或者是 的一些子類;它不需要從字面上看 extend Shape。)我們說Shape是通配符的上界 。

像往常一樣,通過支付 的價格,可以靈活地使用 通配符。這個價格是 現在非法寫入形狀 該方法的主體。例如, 這是不允許的:

public void addRectangle(List<? extends Shape> shapes) { 
    shapes.add(0, new Rectangle()); // Compile-time error! 
} 

您應該 能夠找出爲什麼上面 的代碼是不允許的。 shapes.add()的第二個參數 的類型是?延伸 形狀 - Shape的未知子類型。 由於我們不知道它是什麼類型,我們不知道它是否是超類型 矩形;它可能或可能不是 這樣的超類型,所以 在那裏傳遞一個Rectangle是不安全的。

5

所提供的列表是一些特定類型的車輛的列表(其中,對於參數,我們將參考類型T的緣故),但特定類型T是未知的;它可能是List<Vehicle>,List<Car>等。因此,由於該列表的特定通用類型是未知的,因此不允許調用任何需要特定的T作爲參數的方法。只有不涉及T作爲參數的方法才能被調用。

在List的情況下,這樣做的實際結果是,這可以防止任何事情被添加到列表 - 列表不可寫。另一方面,列表可以讀取,但返回的對象只被稱爲Vehicle

也就是說,未知類型T無法提供給列表,但其已知的超類Vehicle可以由列表返回。

舉例來說,給你的方法:

private static void addCars(List<? extends Vehicle> vcls) { 

你可以想象調用:

List<Car> cars=new ArrayList<Car>(); 
addCars(cars); 

,你直覺應該被允許。然而,由於addCars知道名單隻爲「Vehicle一些亞型」,它不能被允許的對象添加到列表中,因爲下面的調用將被同樣有效:

List<Spaceship> ships=new ArrayList<Spaceship>(); 
addCars(ships); 

由此變得清晰嘗試將Car對象添加到列表中是不對的,因爲它是一個Vehicle對象列表。

0

如果下面可能是可能的..

private static void addCars(List<? extends Vehicle> vcls){ 
     vcls.add(new Car()); 
     vcls.add(new Car()); 
     vcls.add(new Car()); 
    } 

,那麼你可以調用addCars這樣:

List<SpaceShip> ships = new ArrayList<SpaceShip>(); 
addCars(ships); 
0

get和put原則問題:

你可以試試這個

private static void addCars(List<? super Car> vcls){ 
    vcls.add(new Car()); 
    vcls.add(new Car()); 
    vcls.add(new Car()); 

它喜歡;

List<Integer> ints=Arrays.asList(1,2,3); List<? extends Number> nums=ints; double dbl=sum(nums); // ===ok nums.add(3.14); //===compile-time-error

和通配符List<Object> ints=Arrays<Object>.asList(1,"two"); List<? super Integer> nums=ints; double dbl=sum(nums); // ===compile-time-error nums.add(3.14); //===ok

-1

使用王子get和put在通配符 如果通配符與擴展--->使用GET方法 如果使用通配符與超級----> put方法 在這裏,你要添加值到列表(意put方法)。你可以改變代碼

List<? extends Vehicle become List<? super Vehicle> then it will compile legally 
private static void addCars(List<? super Vehicle> vcls){ 
     vcls.add(new Car()); 
     vcls.add(new Car()); 
     vcls.add(new Car()); 
    }