泛型的意義和作用是啥?
簡單的說,意義和作用有:
型別的引數化,就是可以把型別像方法的引數那樣傳遞。這一點意義非凡。
泛型使編譯器可以在編譯期間對型別進行檢查以提高型別安全,減少執行時由於物件型別不匹配引發的異常。
泛型方法,演算法的複用。蠻神奇的。
想要理解為什麼引入泛型,就要知道沒有泛型的麻煩。
我們知道現在的程式開發都是物件導向了,所以程式裡會有很多各種型別的物件,物件多了肯定需要有某種型別的容器來裝。所以就有了一些容器型別,比如陣列、ArrayList、HashMap、TreeSet等。
對於陣列,我們知道需要在使用時指定陣列裝的物件型別,如:
Animal animal[]; Dog dog[];
而對於集合型別容器如ArrayList、HashMap、TreeSet等,它們不但是容器,還提供了一些方法對容器內物件的操作方法,如get,set,sort。這個時候就需要知道容器內放的是什麼型別的物件,才能return或set。
正因為程式開發人員可能把任何型別的物件放進集合容器,所以這些容器在設計的時候只能預設設計成裝Object型別物件。因為Java裡Object是根類。
所以容器就成了類似這個樣子:
public class ListContainer { private Object obj; public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } }
這樣的話,根據多型,容器就能裝任何型別的物件了。不過,取出物件時則需求進行強制型別轉換,轉換成實際的型別。但這樣會有很多型別不安全問題,為什麼呢?因為編譯器沒法幫忙做型別檢查,導致程式碼在執行時易於出現ClassCastException異常。因此,型別安全問題只能由程式設計師自己來把關了,記住各種型別,避免型別轉換錯誤。
ListContainer myContainer = new ListContainer(); myContainer.setObj("123"); ArrayList objectList = new ArrayList(); objectList.add(myContainer); //下面這句編譯時無異常,執行時會ClassCastException異常 Integer myStr = (Integer) ((ListContainer)objectList.get(0)).getObj(); //下面這句ok String myStr = (String) ((ListContainer)objectList.get(0)).getObj();
泛型出場:型別的引數化
利用泛型,重新設計:
public class ListContainer<T> { private T t; public T getObj() { return t; } public void setObj(T t) { this.t= t; } }
這裡<T>的T的型別的引數,具體T指代什麼型別,是String還是Animal還是Dog型別此處不管,而在程式設計師開發時使用到ListContainer時再指定,如:
ListContainer<Dog> myCon=new ListContainer<Dog>();
這種環境下,編譯器就知道ListContainer容器是放Dog型別物件的。並進行型別安全檢查。
myCon.setObj(new Dog())//ok myCon.setObj(“123”);//編譯時提醒型別錯誤
這樣設計的容器在使用時編譯器就可以幫忙做很大一部分的型別安全檢查工作了,這就避免了很多執行時的ClassCastException異常,程式設計師也無需記住各種物件的型別和擔心型別匹配問題了。同時大部分情況下也不用做型別強制轉換工作了。
ListContainer<String> myContainer = new ListContainer<String>(); myContainer.setObj("123"); myContainer.setObj(new Dog());//編譯器就提醒型別異常 ArrayList objectList<ListContainer> = new ArrayList<ListContainer>(); objectList.add(myContainer); Integer myStr = (objectList.get(0)).getObj(); //編譯時提醒型別異常 String myStr = (objectList.get(0)).getObj();
當然泛型的<>裡也可以放多個引數,如:
public class MultiContainer<T,S> { private T t; private S s; ... MultiContainer<String,Dog> multicon=new MultiContainer<String,Dog>();
有界泛型
看看這個泛型和多型的問題,Dog,Cat是Animal的子類:
public void killAll(ArrayList<Animal> animals){...};//Animal容器 ... ArrayList<Animal> animals=new ArrayList<Animal>(); animals.add(new Dog()); animals.add(new Cat()); killAll(animals);//這裡ok ArrayList<Dog> dogs=new ArrayList<Dog>();//Dog是Animal的子類 dogs.add(new Dog()); dogs.add(new Dog()); killAll(dogs);//這裡編譯不通過
在這裡看上去似乎多型不行了。
這裡就要用到有界泛型:
在使用泛型時,我們會有這種需求:需要指定泛型的型別範圍。有界型別就是在型別引數部分指定extends或super關鍵字,這裡的extends也含有implements的功能,分別用上限或下限來限制型別範圍,從而限制泛型的型別邊界。例如:
<T extends Animal>//限定T是Animal的子類 <T super Dog >//限定T是Dog的超類
那麼上面那個多型問題就變成:
public void killAll(ArrayList<T extends Animal> animals){...};
解決了。
<T extends Object&Comparable&Serializable>
多個限定時我們可以使用&來進行分割,這時關鍵詞只能使用extends。與多重繼承類似,這裡只有一個類其他都是介面。
泛型方法
有時,我們設計的方法可能其引數型別是不限定的。這種場景如果用過載方法的方式來做的話,演算法重複,不是最好的方案。此時泛型方法就可以解決此類問題。
如Calculator的add方法:
public static <N extends Number> double add(N a, N b){ double sum = 0; sum = a.doubleValue() + b.doubleValue(); return sum; }
如果用過載來做的話,要很多重複程式碼了。
相關文章
- 泛型--泛型萬用字元和泛型的上下限泛型字元
- 泛型類和泛型方法泛型
- TypeScript 泛型介面和泛型類TypeScript泛型
- C# 泛型集合的自定義型別排序C#泛型型別排序
- 訊息佇列的作用是啥佇列
- TypeScript 基本型別和泛型的使用TypeScript型別泛型
- Kotlin 泛型中的 in 和 outKotlin泛型
- 理解C#泛型運作原理C#泛型
- 泛型類、泛型方法及泛型應用泛型
- 07.集合和泛型泛型
- 【java】【泛型】泛型geneticJava泛型
- rust trait 關聯型別和泛型的區別RustAI型別泛型
- 實踐和思考的重要意義
- 泛型類、泛型方法、型別萬用字元的使用泛型型別字元
- 【.NET】利用 IL 魔法實現隨心隨意的泛型約束泛型
- Go 泛型之泛型約束Go泛型
- 泛型方法、初始集合和集合的遍歷泛型
- 泛型泛型
- 軟體測試的目的和意義
- MySQL int型別長度的意義是什麼MySql型別
- 淺談多型機制的意義及實現多型
- 談談 "JS 和 設計泛型"JS泛型
- TypeScript學習(四)—— 介面和泛型TypeScript泛型
- 泛型最佳實踐:Go泛型設計者教你如何用泛型泛型Go
- Kotlin中的泛型Kotlin泛型
- 泛型物件的使用泛型物件
- Java中的泛型Java泛型
- 泛型擦除的原理泛型
- Java泛型中<?> 和 <? extends Object>的異同分析Java泛型Object
- C#中的介面和泛型集合探討C#泛型
- 人生的意義
- Java培訓分享void的用法和意義Java
- Java技術分享:void的用法和意義Java
- TypeScript 泛型型別TypeScript泛型型別
- 型別 VS 泛型型別泛型
- 泛型(一)泛型
- 泛型(三)泛型
- 泛型(二)泛型
- 泛型(四)泛型