泛型上下邊界
作用
先從通配符'?'說起焙蹭,已知通配符可以是任意類類型旁振,在實(shí)際業(yè)務(wù)或功中在使用通配符時(shí)會(huì)遇到很多安全問題如:傳入的泛型類沒有特定的方法或?qū)傩栽游埃愋娃D(zhuǎn)換錯(cuò)誤等等祭芦。所以為了防止這些問題的發(fā)生筷笨,就有了上下邊界,用于指定通配符的范圍龟劲。
上邊界
關(guān)鍵字 ==extends== : 通配符('?')類型必須繼承指定的某個(gè)類(class)胃夏,或?qū)崿F(xiàn)某個(gè)接口(interface)。
// 上邊界指定格式
? extends 指定類型(可以是類也可以是接口)
// 要求
class ? extends 指定類
// 或
class ? implements 指定接口
// 要求繼承指定類且必須實(shí)現(xiàn)指定接口
? extends 指定類&指定接口
// 如下
? extends A&B
==范圍== : 指定類型或指定類型的子類
示例
一圖勝千言:
示例類:
// 食物
class Food { }
// 肉
class Meat extends Food { }
// 豬肉
class Pork extends Meat { }
// 牛肉
class Beef extends Meat { }
// 水果
class Fruit extends Food { }
// 蘋果
class Apple extends Fruit { }
// 香蕉
class Banana extends Fruit { }
// 青蘋果
class GreenApple extends Apple { }
// 紅蘋果
class RedApple extends Apple { }
// 容器類(裝食物用)
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 static void eatFruit(Container<? extends Fruit> container) {
// 取出水果
Fruit obj = container.getObj();
// 打印
System.out.println("我正在吃" + obj.getName());
}
// 吃肉
public static void eatMeat(Container<? extends Meat> container) {
// 取出肉
Meat obj = container.getObj();
// 打印
System.out.println("我正在吃" + obj.getName());
}
// 主函數(shù)
public static void main(String[] strs) {
// 水果盤
Container<Fruit> fruits = new Container<Fruit>();
// 香蕉籃
Container<Banana> bananas = new Container<Banana>();
// 菜盤
Container<Pork> porks = new Container<Pork>();
// 一個(gè)水果
Fruit fruit = new Fruit("水果");
// 一根香蕉
Banana banana = new Banana("香蕉");
// 一個(gè)蘋果
Apple apple = new Apple("蘋果");
// 一塊豬肉
Pork pork = new Pork("土豬肉");
// 把洗好的水果裝盤
fruits.setObj(fruit);
fruits.setObj(apple);
bananas.setObj(banana);
// 把炒好的土豬肉裝盤
porks.setObj(pork);
// 調(diào)用一下吃水果的方法進(jìn)行吃水果
eatFruit(fruits);
// 調(diào)用一下吃水果的方法進(jìn)行吃香蕉
eatFruit(bananas);
// 調(diào)用一下吃水果的方法進(jìn)行吃豬肉昌跌,這個(gè)時(shí)候編譯器就會(huì)提示異常
// 因?yàn)槌运姆椒ㄟM(jìn)行了上限的設(shè)置
// eatFruit(porks);
// 所以吃肉的就得使用吃肉的方法
eatMeat(porks);
}
==注== : 當(dāng)設(shè)定上限后只能讀取內(nèi)容(即:get)仰禀,不能往里添加內(nèi)容(即:set)
例:在上述吃水果的方法中不能往里添加新的水果,否則編譯器將會(huì)提示異常
下邊界
關(guān)鍵字 ==super== : 指定的類型不能小于操作的類蚕愤,即指定類及其父類
? super 指定類型
==范圍== : 指定類型或指定類型的父類...父類的父類最終至Object答恶,且不能為任意父類的其他子類
示例
一圖勝千言:
示例類:
// 上限中的示例類
代碼邏輯:
// 加菜
public static void addDish(Container<? super Meat> container) {
// 裝土豬肉
container.setObj(new Pork("土豬肉"));
// 裝牛肉
container.setObj(new Pork("烤肥牛"));
}
// 主函數(shù)
public static void main(String[] strs) {
// 菜盤
Container<Food> foods = new Container<Food>();
// 專用裝肉盤
Container<Meat> meats = new Container<Meat>();
// 水果籃
Container<Fruit> fruits = new Container<Fruit>();
// 我們吃飯的時(shí)候菜吃完囊榜,所以我們加菜
// 廚師準(zhǔn)備用盤子裝菜
// 用菜盤裝菜
addDish(foods);
// 用專用裝肉盤裝菜
addDish(meats);
// 但不能用水果籃裝菜,一使用編譯器就會(huì)提示我們異常
// addDish(fruits);
}
==注== : 當(dāng)設(shè)定下限后只能添加內(nèi)容(即:set)亥宿,而取的話只能為Object類型卸勺,不能為具體類型
例:在上述裝菜的方法中不能從盤子里取出具體菜(即:只能是Object類型的菜),否則編譯器將會(huì)提示異常
總結(jié)
這兩種方式基本上解決了之前所說的問題烫扼,但是同時(shí)曙求,也有一定的限制。
1.上限<? extends T>不能往里存映企,只能往外取 (即:只能get)
因?yàn)榫幾g器只知道容器里的是Fruit或者Fruit的子類悟狱,但不知道它具體是什么類型,所以存的時(shí)候堰氓,無法判斷是否要存入的數(shù)據(jù)的類型與容器種的類型一致挤渐,所以會(huì)拒絕set操作。
2.下限<? super T>往外取只能賦值給Object變量双絮,不影響往里存
因?yàn)榫幾g器只知道它是Fruit或者它的父類浴麻,這樣實(shí)際上是放松了類型限制,F(xiàn)ruit的父類一直到Object類型的對(duì)象都可以往里存囤攀,但是取的時(shí)候软免,就只能當(dāng)成Object對(duì)象使用了。
所以如果需要經(jīng)常往外讀焚挠,則使用<? extends T>膏萧,如果需要經(jīng)常往外取,則使用<? super T>蝌衔。