簡單的說,意義和作用有:
類型的參數化,就是可以把類型像方法的參數那樣傳遞。這一點意義非凡。
泛型使編譯器可以在編譯期間對類型進行檢查以提高類型安全,減少運行時由於對象類型不匹配引發的異常。
泛型方法,算法的復用。蠻神奇的。
想要理解為什么引入泛型,就要知道沒有泛型的麻煩。
我們知道現在的程序開發都是面向對象了,所以程序里會有很多各種類型的對象,對象多了肯定需要有某種類型的容器來裝。所以就有了一些容器類型,比如數組、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); }