[英]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.