簡體   English   中英

Java通用通配符問題:列表<? extends A>

[英]Java Generics WildCard Question: List<? 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是擴展X的所有X的List的超類型。 對?

謝謝

編輯:我得到的錯誤(編譯時):類型List中的方法add(capture#2-of擴展Vehicle)不適用於自變量(Car)。

方法參數在子類型中是互變的,並且根據通配符的定義,對於擴展Vehicle Foo<T>每個類型T都是Foo<* extends Vehicle>的子類型。 這意味着當您只關心返回類型時,通配符非常有用,但是當您想要將類型的值傳遞給方法時,在這種情況下不起作用。

問題是用戶可能會嘗試呼叫

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

如果要編譯您的代碼,則l將是包含3輛汽車的宇宙飛船的列表。 顯然沒有好處。

這是為什么出現編譯錯誤的指針。 特別,

列表是有界通配符的示例。 代表未知類型,就像我們之前看到的通配符一樣。 但是,在這種情況下,我們知道該未知類型實際上是Shape的子類型。 (注意:它可以是Shape本身,也可以是某些子類;它不需要擴展Shape。)我們說Shape是通配符的上限。

與往常一樣,使用通配符的靈活性要付出一定的代價。 代價是,現在在方法主體中寫入形狀是非法的。 例如,這是不允許的:

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

您應該能夠弄清楚為什么不允許使用上面的代碼。 shapes.add()的第二個參數的類型是? 擴展Shape-Shape的未知子類型。 由於我們不知道它是什么類型,所以我們不知道它是否是Rectangle的超類型。 它可能是也可能不是這種超類型,因此在此處傳遞Rectangle是不安全的。

提供的List是某些特定類型的Vehicle的列表(在這里,為了便於討論,我們將類型稱為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);

由此變得清楚,它必須是錯誤的做法車的物體所列表假借添加到列表Vehicle對象。

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

應該

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

這將修復編譯時錯誤。

編輯: 在這里閱讀。

參數的類型是? extends Vehicle ? extends Vehicle ,這意味着一個未知的亞型Vehicle 由於我們不知道它是什么類型,所以我們不知道它是否是Car的超類型; 它可能是也可能不是這種超類型,因此在那兒通過Car並不安全。

閱讀本教程的第7頁。

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

您可以使用:

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

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

要么

private static void addCars(List<Vehicle> vcls)

獲取和放置原則問題:

你可以試試這個

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通配符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 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

如果可能的話..

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);

使用prince獲取並放入通配符如果擴展名使用通配符--->使用get方法如果使用Super的通配符---->使用put方法在這里,您想在List中添加值(意思是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());
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM