上一篇 【泛型】通配符與嵌套
通配符可以是任意類類型,在實際業務中使用通配符時,可能會遇到很多安全問題,如傳入的泛型類沒有特定的方法或屬性,類型轉換錯誤等。爲了防止這些問題的發生,就有了上下邊界,用於指定通配符的範圍。
1 泛型上限extends
上限extends指定的類型必須是繼承某個類,或者某個接口,即<=,如
? extends Fruit
T extends List
// 容器類(裝食物用)
public class Container<T> {
private T obj;
public Container(){}
public Container(T obj){
this.obj = obj;
}
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
示例類:
public class Food<T> {
private T obj;
public Food(T obj){
this.obj = obj;
}
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
public class Fruit<T> extends Food{
public Fruit(T obj) {
super(obj);
}
}
public class Apple<T> extends Fruit{
public Apple(T obj) {
super(obj);
}
}
public class Banana<T> extends Fruit{
public Banana(T obj) {
super(obj);
}
}
public class Meat<T> extends Food{
public Meat(T obj) {
super(obj);
}
}
public class Pork<T> extends Meat{
public Pork(T obj) {
super(obj);
}
}
調用
// 主函數
public static void main(String[] strs) {
// 水果盤
Container<Fruit> fruits = new Container<Fruit>();
// 香蕉籃
Container<Banana> bananas = new Container<Banana>();
// 菜盤
Container<Pork> porks = new Container<Pork>();
// 一個水果
Fruit fruit = new Fruit("水果");
// 一根香蕉
Banana banana = new Banana("香蕉");
// 一個蘋果
Apple apple = new Apple("蘋果");
// 一塊豬肉
Pork pork = new Pork("土豬肉");
Meat meat = new Meat("肉");
// 把洗好的水果裝盤
fruits.setObj(fruit);
fruits.setObj(apple);
bananas.setObj(banana);
Container<? super Meat> container = new Container<>();
container.setObj(pork);
Container<? extends Meat> container1= new Container<>();
//container1.setObj(meat);會報錯
// 把炒好的土豬肉裝盤
porks.setObj(pork);
}
2 泛型下限super
? super 指定類型
指定類型不能小於操作的類,即指定類型或指定類型的父類…父類的父類最終至Object,且不能爲任意父類的其他子類。
// 加菜
public static void addDish(Container<? super Meat> container) {
// 裝土豬肉
container.setObj(new Pork("土豬肉"));
// 裝牛肉
container.setObj(new Pork("烤肥牛"));
}
// 主函數
public static void main(String[] strs) {
// 菜盤
Container<Food> foods = new Container<Food>();
// 專用裝肉盤
Container<Meat> meats = new Container<Meat>();
// 水果籃
Container<Fruit> fruits = new Container<Fruit>();
// 我們喫飯的時候菜喫完,所以我們加菜
// 廚師準備用盤子裝菜
// 用菜盤裝菜
addDish(foods);
// 用專用裝肉盤裝菜
addDish(meats);
// 但不能用水果籃裝菜,一使用編譯器就會提示我們異常
// addDish(fruits);
}
總結
這兩種方式基本上解決了之前所說的問題,但是同時,也有一定的限制。
1.上限<? extends T>不能往裏存,只能往外取 (即:只能get)
因爲編譯器只知道容器裏的是Fruit或者Fruit的子類,但不知道它具體是什麼類型,所以存的時候,無法判斷是否要存入的數據的類型與容器種的類型一致,所以會拒絕set操作。
2.下限<? super T>往外取只能賦值給Object變量,不影響往裏存
因爲編譯器只知道它是Fruit或者它的父類,這樣實際上是放鬆了類型限制,Fruit的父類一直到Object類型的對象都可以往裏存,但是取的時候,就只能當成Object對象使用了。
所以如果需要經常往外讀,則使用<? extends T>,如果需要經常往外取,則使用<? super T>。
參考:
- https://www.jianshu.com/p/0c318bb54502
- https://zhuanlan.zhihu.com/p/97517915