簡單的說,意義和作用有:
類型的參數(shù)化,就是可以把類型像方法的參數(shù)那樣傳遞。這一點意義非凡。
泛型使編譯器可以在編譯期間對類型進行檢查以提高類型安全,減少運行時由于對象類型不匹配引發(fā)的異常。
泛型方法,算法的復用。蠻神奇的。
想要理解為什么引入泛型,就要知道沒有泛型的麻煩。
我們知道現(xiàn)在的程序開發(fā)都是面向?qū)ο罅?,所以程序里會有很多各種類型的對象,對象多了肯定需要有某種類型的容器來裝。所以就有了一些容器類型,比如數(shù)組、ArrayList、HashMap、TreeSet等。
對于數(shù)組,我們知道需要在使用時指定數(shù)組裝的對象類型,如:
Animal?animal[];
Dog dog[];
而對于集合類型容器如ArrayList、HashMap、TreeSet等,它們不但是裝對象,還提供了一些方法對容器內(nèi)的對象進行操作,如get,set,sort等。這個時候就需要知道容器內(nèi)放的是什么類型的對象,這樣才能return或set啊。
正因為程序開發(fā)人員可能把任何類型的對象放進集合容器,所以這些容器在設(shè)計的時候只能默認設(shè)計成裝Object類型對象。因為Java里Object是根類。
所以容器就成了類似這個樣子:
public class ListContainer {
private?Object?obj;
public?Object?getObj()?{
return?obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
這樣的話,根據(jù)多態(tài),容器就能裝任何類型的對象了。不過,取出對象時則需求進行強制類型轉(zhuǎn)換,轉(zhuǎn)換成實際的類型。但這樣會有很多類型不安全問題,為什么呢?因為編譯器沒法幫忙做類型檢查,導致代碼在運行時易于出現(xiàn)ClassCastException異常。因此,類型安全問題只能由程序員自己來把關(guān)了,記住各種對象屬于什么類型,避免類型轉(zhuǎn)換的錯誤。
ListContainer myContainer = new ListContainer();
myContainer.setObj("123");
ArrayList?objectList?=?new?ArrayList();
objectList.add(myContainer);
//下面這句編譯時無異常,運行時會ClassCastException異常,也許程序員忘了“123”,123
Integer?myStr?=?(Integer)?((ListContainer)objectList.get(0)).getObj();
//下面這句ok
String?myStr?=?(String)?((ListContainer)objectList.get(0)).getObj();
泛型出場:類型的參數(shù)化
利用泛型,重新設(shè)計:
public class ListContainer<T> {
private?T?t;
public?T?getObj()?{
return?t;
}
public void setObj(T t) {
this.t= t;
}
}
這里<T>的T就是類型參數(shù),具體T指代什么類型,是String還是Animal還是Dog類型此處不管,而是在程序員開發(fā)時使用到ListContainer時再指定,如:
ListContainer<Dog> myCon=new ListContainer<Dog>();
此刻,編譯器就知道ListContainer容器是放Dog類型對象的。并進行類型安全檢查。
myCon.setObj(new Dog())//ok
myCon.setObj(“123”);//編譯時提醒類型錯誤
這樣設(shè)計的容器在使用時編譯器就可以幫忙做很大一部分的類型安全檢查工作了,這就避免了很多運行時的ClassCastException異常,程序員也無需記住各種對象的類型和擔心類型匹配問題了。同時大部分情況下也不用做類型強制轉(zhuǎn)換工作了。
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();//無需類型強制轉(zhuǎn)換
當然泛型的<>里也可以放多個參數(shù),如:
public class MultiContainer<T,S>?{
private?T t;
private?S s;
...
MultiContainer<String,Dog> multicon=new MultiContainer<String,Dog>();
有界泛型
看看這個泛型和多態(tài)的問題,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);//這里編譯不通過
在這里看上去似乎多態(tài)不行了。
這里就要用到有界泛型:
在使用泛型時,我們會有這種需求:需要指定泛型的類型范圍。有界類型就是在類型參數(shù)部分指定extends或super關(guān)鍵字,這里的extends也含有implements的功能,分別用上限或下限來限制類型范圍,從而限制泛型的類型邊界。例如:
<T extends Animal>//限定T是Animal的子類
<T super Dog >//限定T是Dog的超類
那么上面那個多態(tài)問題就變成:
public void killAll(ArrayList<T extends Animal> animals){...};
問題解決了。
多個限定時我們可以使用&來進行分割,這時關(guān)鍵詞只能使用extends。與多重繼承類似,這里只可以有一個類,其他都是接口。
<T extends Object&Comparable&Serializable>
泛型方法
有時,我們設(shè)計的方法可能其參數(shù)類型是不限定的。這種場景如果用重載方法的方式來做的話,算法重復,不是最好的方案。此時泛型方法就可以解決此類問題。
如Calculator的add方法:
public static <N extends Number> double add(N a, N b){
double?sum?=?0;
sum?=?a.doubleValue()?+?b.doubleValue();
return?sum;
}
如果用重載來做的話,要很多重復代碼了。
本文結(jié)束