泛型:
類型參數化,解決不確定對象類型的問題。好處是代碼複用性高。
作用範圍:
類,方法,接口。 編譯器通過識別尖括號內的字母來解析泛型。
約定俗成的泛型名: E (Element 集合中元素) , T (the Type of Object)某個類。
應用舉例:
在沒有使用泛型前:
//未使用 泛型,會出現強制類型轉換報錯風險
public static Object heap (Object food)
{
System.out.println(food + "is done");
return food;
}
public static void main(String[] args) {
// 加熱肉
Meat meat = new Meat();
Meat me = (Meat)Stove.heap(meat);
// 加熱湯
Soup soup = new Soup();
Soup soup = (Soup)Stove.heap(soup);
}
這裏用 Object 來避免爲每種食物都寫一種加熱的方法,但根據墨菲定律,會發生強制類型轉換的危險。
使用泛型:
public static T heap (T food)
{
System.out.println(food + "is done");
return food;
}
public static void main(String[] args) {
// 加熱肉
Meat meat = new Meat();
Meat me = Stove.heap(meat);
// 加熱湯
Soup soup = new Soup();
Soup soup = Stove.heap(soup);
}
好處:不會出現類型轉換的錯誤,代碼複用性高。
泛型與集合的聯合使用:
List, List , List<?> 這三個對象有啥區別?直接看代碼:
public static void main(String[] args)
{
// 1 無泛型約束
List a1 = new ArrayList<>();
a1.add(new Integer(1));
a1.add(new Object());
a1.add("hello");
//將 a1 的引用賦值給 a2 , 但是在接受其他泛型賦值時會出現編譯錯誤
List<Object> a2 = a1;
a2.add(new Integer(1));
a2.add(new Object());
a2.add("hello");
//將 a1 的引用賦值給 a3
List<Integer> a3 = a1;
a3.add(new Integer(1));
a3.add(new Object());
a3.add("hello");
//將 a1 的引用賦值給 a4
List<?>a4 = a1;
a4.add(new Object());
a4.remove(2);
a4.clear();
}
第一段,因爲不存在類型約束,所以向集合內添加任何對象都不會報錯。
第二段,泛型是 Object ,也可以添加任何類型的對象,但唯一的區別是在進行類型轉化時會報錯的風險。
第三段,泛型是 Integer, 此時就有了約束,不是 Integer 類型的就無法成功添加其中
第四段, 添加通配符,只能進行 刪除和清理操作,不能進行添加操作。
我們可以發現,這幾種用法只能添加一種泛型約束,那有沒有添加多種泛型約束呢?
List<? extend> 與 List<? super> 的區別:
創建三個類,動物,狗,柯基。(柯基繼承狗,狗繼承動物):
List<Animal> animal = new ArrayList<>();
List<Dog> dog = new ArrayList<>();
List<keji>kj = new ArrayList<>();
animal.add(new Animal());
dog.add(new Dog());
kj.add(new keji());
// 只能賦值給 Dog 和子類(keji),所以此時會報錯
List<? extends Dog>extendsDogFromAnimal = animal;
//只能賦值給 Dog 和父類
List<? super Dog> superDogFromAnimal = animal;
// 不能添加任何元素,會報錯
extendsDogFromAnimal.add(new Dog());
// 可以添加元素
superDogFromAnimal .add(new Dog());
// 類型丟失,只能返回 對象類型
Object ob = superDogFromAnimal.get(0);
//返回類型不會擦除,子類對象被擦除
Dog do = extendsDogFromAnimal.get(0);
通過上面的代碼可以發現,
List<?extends T>只能賦值給 自己和子類,上限是 T,不能添加元素,能返回父類和本類對象,但子類對象會被擦除。
List<? super T> 只能賦值自己和父類,下限是 T , 能添加元素,能返回 Object 類型的元素。
應用場景:
使用 <? extend T> 是 put 的功能受限,<? super T>是 get , 功能受限。
進行匿名投票時,向箱子裏投票,在想拿出來時就會被告知 “”您以參與投票!“”,而這裏就可以是使用 <? super T>
還可以拿上面的例子來說明 <?extend T> 適合放數據,<? super T>適合拿數據。
場景一: 當我們想放進一隻狗時,你會向哪個箱子選擇,是 動物箱子,還是狗箱子,抑或是 柯基箱子?
場景二: 當我們想拿出一隻動物時,你會向哪個箱子選擇,是 動物箱子,還是狗箱子,抑或是 柯基箱子?
總結:
花了些許的時間,介紹了 泛型,以及泛型和集合的聯合使用。看了一位大佬的視屏說如果你想提高編程能力,使代碼更富有條理性,利用 java 的泛型和反射 API 是最好的辦法。