java泛型學(xué)習(xí)筆記
本文為從網(wǎng)上東拼西湊的java泛型學(xué)習(xí)筆記,摘出了我認(rèn)為有價(jià)值的部分,尚未整理,先放在這里備份。
泛型在類(lèi)、接口和方法中定義,在實(shí)現(xiàn)(extends,implements)和實(shí)例化時(shí)使用。
定義類(lèi)或接口時(shí),使用"<E extends Fruit>"這種形式,之后就可以在類(lèi)中對(duì)E進(jìn)行操作。
定義方法所接收的參數(shù)時(shí),使用"List<? extends Fruit>"這種形式,就可以接收這個(gè)范圍的List做參數(shù)。
實(shí)例化時(shí),不能使用問(wèn)號(hào)這種形式來(lái)指定泛型——不能new List<? extends Apple>();
繼承或?qū)崿F(xiàn)時(shí),也不能使用問(wèn)號(hào)這種形式來(lái)指定泛型——不能public interface MyList extends List<? extends Apple>
關(guān)于extends和super關(guān)鍵字的PECS(producer-extends, consumer-super)原則:
如果參數(shù)化類(lèi)型表示一個(gè)T生產(chǎn)者,就使用<? extends T>,因?yàn)樗荒躦et,用于將數(shù)據(jù)從生產(chǎn)者取出(只要生產(chǎn)者可以生產(chǎn)T的子類(lèi),那就一定可以生產(chǎn)T);
如果參數(shù)化類(lèi)型表示一個(gè)T消費(fèi)者,就使用<? super T>,因?yàn)樗荒躠dd,用于將數(shù)據(jù)add入消費(fèi)者(只要消費(fèi)者可以消費(fèi)T的超類(lèi),那就一定可以消費(fèi)T)。
泛型的繼承原則:
1,相同類(lèi)型參數(shù)的泛型類(lèi)的關(guān)系取決于泛型類(lèi)自身的繼承體系結(jié)構(gòu)。即List<String>是Collection<String> 的子類(lèi)型,List<String>可以替換Collection<String>。這種情況也適用于帶有上下界的類(lèi)型聲明。
2,當(dāng)泛型類(lèi)的類(lèi)型聲明中使用了通配符的時(shí)候, 其子類(lèi)型可以在兩個(gè)維度上分別展開(kāi)。如對(duì)Collection<? extends Number>來(lái)說(shuō),其子類(lèi)型可以在Collection這個(gè)維度上展開(kāi),即List<? extends Number>和Set<? extends Number>等;也可以在Number這個(gè)層次上展開(kāi),即Collection<Double>和 Collection<Integer>等。如此循環(huán)下去,ArrayList<Long>和 HashSet<Double>等也都算是Collection<? extends Number>的子類(lèi)型。
3,如果泛型類(lèi)中包含多個(gè)類(lèi)型參數(shù),則對(duì)于每個(gè)類(lèi)型參數(shù)分別應(yīng)用上面的規(guī)則。
泛型的定義原則:
1,泛型類(lèi)與一般的Java類(lèi)基本相同,只是在類(lèi)和接口定義上多出來(lái)了用<>聲明的類(lèi)型參數(shù)。
2,一個(gè)類(lèi)可以有多個(gè)類(lèi)型參數(shù),如 MyClass<X, Y, Z>。
3,每個(gè)類(lèi)型參數(shù)在聲明的時(shí)候可以指定上界。
4,所聲明的類(lèi)型參數(shù)在Java類(lèi)中可以像一般的類(lèi)型一樣作為方法的參數(shù)和返回值,或是作為域和局部變量的類(lèi)型。
5,由于類(lèi)型擦除機(jī)制,類(lèi)型參數(shù)并不能用來(lái)創(chuàng)建對(duì)象或是作為靜態(tài)變量的類(lèi)型。
如果沒(méi)有向后兼容性顧慮,那么Collection框架會(huì)這么設(shè)計(jì)(而實(shí)際上不是):
interface Collection<E> {
public T[] toArray(Class<T super E> elementClass);
}
這樣很通用,假設(shè)E是Apple類(lèi),那么傳入任何一個(gè)Apple的父類(lèi)(例如Fruit)都合法,會(huì)返回Fruit數(shù)組。
如果傳入Object,那就相當(dāng)于現(xiàn)在Collection接口的Object[] toArray()方法了。
同樣,如果沒(méi)有向后兼容性顧慮,那么Collection框架的remove和removeAll會(huì)這么寫(xiě)(而實(shí)際上不是):
interface Collection<E> {
public boolean remove(E e); // not really
public void removeAll(Collection<? extends E> c); // not really
}
缺陷是:只能傳入當(dāng)前泛型的兼容類(lèi)型,不能傳入任意Object或Collection<Object>——這樣無(wú)法到兼容之前的API定義。
不能創(chuàng)建泛型類(lèi)型的對(duì)象,因?yàn)榫幾g器不知道要調(diào)用什么構(gòu)造函數(shù)。如果泛型類(lèi)需要構(gòu)造用泛型類(lèi)型參數(shù)來(lái)指定類(lèi)型的對(duì)象,那么構(gòu)造函數(shù)應(yīng)該接受類(lèi)文字(Foo.class)并將它們保存起來(lái),以便通過(guò)反射創(chuàng)建實(shí)例(類(lèi)似于上述Collection的toArray方法)
類(lèi)型安全的異構(gòu)容器(內(nèi)部用Map<Class<?>,Object>來(lái)實(shí)現(xiàn)):
public class Favorites {
public <T> void putFavorite(Class<T> type,T instance);
public <T> T getFavorite(Class<T> type);
}
public <T> void putFavorite(Class<T> type,T instance);
public <T> T getFavorite(Class<T> type);
}
例如用于數(shù)據(jù)庫(kù)不定數(shù)目的columns:每個(gè)cell的數(shù)據(jù)都以這種方式指定類(lèi)型保存Favorites到容器中,再將此容器當(dāng)作元素保存到更上層的結(jié)果容器中。
不能實(shí)例化泛型類(lèi)型的數(shù)組:new List<String>[3]是不合法的。
但是在可以確定類(lèi)型的情況下,將Object數(shù)組強(qiáng)制轉(zhuǎn)換成泛型數(shù)組是合法的,例如ArrayList的源碼:return (T[]) Arrays.copyOf(elementData, size, a.getClass());
泛型方法:
聲明方法時(shí)將用到的類(lèi)型參數(shù)列在前面即可(所在類(lèi)上不需要定義此參數(shù)類(lèi)型):
public static <E> Set<E> union(Set<E> s1,Set<E> s2) ;
靜態(tài)工具方法特別適合這樣寫(xiě)。
一般來(lái)說(shuō)編寫(xiě)java泛型方法時(shí),返回值類(lèi)型和至少一個(gè)參數(shù)類(lèi)型應(yīng)該是泛型,而且類(lèi)型應(yīng)該是一致的,如果只有返回值類(lèi)型或參數(shù)類(lèi)型之一使用了泛型,這個(gè)泛型方法的使用就大大的限制了,基本限制到跟不用泛型一樣的程度。
第一種:public static <T extends CommonService> T getService(Class<T> clazz);
NoticeService noticeService=CommonService.getService(NoticeService.class);//正確的使用第一種泛型方法,不會(huì)出現(xiàn)編譯錯(cuò)誤。
NoticeService noticeService=CommonService.getService(UserService.class);//不正確的使用第一種泛型方法,會(huì)出現(xiàn)編譯錯(cuò)誤。
第二種:public static <T> T getService(Class<? extends CommonService> clazz);
NoticeService noticeService=CommonService.getService(NoticeService.class);//正確的使用第二種泛型方法,不會(huì)出現(xiàn)編譯錯(cuò)誤,邏輯也正確,運(yùn)行時(shí)不會(huì)出現(xiàn)異常。
NoticeService noticeService=CommonService.getService(UserService.class);//不正確的使用第二種泛型方法,不會(huì)出現(xiàn)編譯錯(cuò)誤,但邏輯不正確,運(yùn)行時(shí)會(huì)出現(xiàn)異常,危險(xiǎn)!
--
網(wǎng)上不少關(guān)于泛型的說(shuō)明很好,可是不知道是不是jdk版本問(wèn)題,居然沒(méi)辦法實(shí)際通過(guò)編譯,于是我只好自己在jdk1.7下一一重寫(xiě)和嘗試,最后加上總結(jié)和注釋?zhuān)缦拢?/div>
1 public class Fruit{
2
3 }
2
3 }
1 public class Apple extends Fruit {
2
3 }
2
3 }
1 public class Basket{
2 //從泛型化的list中取出Fruit,里面的元素一定是Fruit的子類(lèi),所以都可以當(dāng)作Fruit返回
3 public static Fruit getFruit(List<? extends Fruit> list){
4 return list.get(0);
5 }
6
7 //將Apple放入泛型化的list(這個(gè)list可以接收的類(lèi)型是Apple的父類(lèi),所以一定可以接收apple或apple的子類(lèi))
8 public static void addApple(List<? super Apple> list,Apple apple){
9 list.add(apple);
10 }
11 }
2 //從泛型化的list中取出Fruit,里面的元素一定是Fruit的子類(lèi),所以都可以當(dāng)作Fruit返回
3 public static Fruit getFruit(List<? extends Fruit> list){
4 return list.get(0);
5 }
6
7 //將Apple放入泛型化的list(這個(gè)list可以接收的類(lèi)型是Apple的父類(lèi),所以一定可以接收apple或apple的子類(lèi))
8 public static void addApple(List<? super Apple> list,Apple apple){
9 list.add(apple);
10 }
11 }
1 public class GenericTester extends AbstractTester{
2 public void test1(){
3 List<Apple> appleList = new ArrayList<Apple>();
4 appleList.add(new Apple()); //先定義List<Apple>,填入元素
5 Fruit fruit=Basket.getFruit(appleList); //List<Apple>類(lèi)型符合List<? extends Fruit>的要求
6 }
7
8 public void test2(){
9 List<Fruit> fruitList=new ArrayList<Fruit>();
10 Basket.addApple(fruitList, new Apple()); //List<Fruit>符合List<? super Apple>的要求
11 }
12 }
2 public void test1(){
3 List<Apple> appleList = new ArrayList<Apple>();
4 appleList.add(new Apple()); //先定義List<Apple>,填入元素
5 Fruit fruit=Basket.getFruit(appleList); //List<Apple>類(lèi)型符合List<? extends Fruit>的要求
6 }
7
8 public void test2(){
9 List<Fruit> fruitList=new ArrayList<Fruit>();
10 Basket.addApple(fruitList, new Apple()); //List<Fruit>符合List<? super Apple>的要求
11 }
12 }
1 public class GenericCompileTester {
2 public void addInteger1(List<?> list){
3 list.add(1); //編譯錯(cuò)誤,因?yàn)閱?wèn)號(hào)代表“編譯時(shí)不確定,但運(yùn)行時(shí)確定”的類(lèi)型,Integer不一定符合要求,其實(shí)這里相當(dāng)于<? extends Object>
4 }
5
6 public void addInteger2(List<Object> list){
7 list.add(1); //編譯通過(guò),因?yàn)榫幾g時(shí)和運(yùn)行時(shí)都確定了是Object類(lèi)型,Integer肯定符合要求
8 }
9
10 public void test(){
11 List<String> list=new ArrayList<String>();
12 addInteger2(list); //編譯錯(cuò)誤,因?yàn)長(zhǎng)ist<String>不能當(dāng)作List<Object>傳入,否則數(shù)字1將被當(dāng)作String添加,類(lèi)型將不再安全
13 }
14
15 public void test3(){
16 List<? extends Fruit> fruitList=new ArrayList<Apple>();
17 fruitList.add(new Apple()); //編譯錯(cuò)誤,編譯器無(wú)法確定List允許的是Fruit的具體哪一個(gè)子類(lèi)
18 fruitList.add(new Fruit()); //編譯錯(cuò)誤,編譯器無(wú)法確定List允許的是Fruit的具體哪一個(gè)子類(lèi)
19 }
20
21 public void test4(){
22 List<? super Apple> fruitList=new ArrayList<Fruit>();
23 fruitList.add(new Apple()); //編譯通過(guò),Apple和Apple的子類(lèi)都可以保證符合條件要求
24 fruitList.add(new Fruit()); //編譯錯(cuò)誤,編譯器無(wú)法確定List允許的是Apple的具體那個(gè)級(jí)別的父類(lèi)或接口
25 }
26 }
2 public void addInteger1(List<?> list){
3 list.add(1); //編譯錯(cuò)誤,因?yàn)閱?wèn)號(hào)代表“編譯時(shí)不確定,但運(yùn)行時(shí)確定”的類(lèi)型,Integer不一定符合要求,其實(shí)這里相當(dāng)于<? extends Object>
4 }
5
6 public void addInteger2(List<Object> list){
7 list.add(1); //編譯通過(guò),因?yàn)榫幾g時(shí)和運(yùn)行時(shí)都確定了是Object類(lèi)型,Integer肯定符合要求
8 }
9
10 public void test(){
11 List<String> list=new ArrayList<String>();
12 addInteger2(list); //編譯錯(cuò)誤,因?yàn)長(zhǎng)ist<String>不能當(dāng)作List<Object>傳入,否則數(shù)字1將被當(dāng)作String添加,類(lèi)型將不再安全
13 }
14
15 public void test3(){
16 List<? extends Fruit> fruitList=new ArrayList<Apple>();
17 fruitList.add(new Apple()); //編譯錯(cuò)誤,編譯器無(wú)法確定List允許的是Fruit的具體哪一個(gè)子類(lèi)
18 fruitList.add(new Fruit()); //編譯錯(cuò)誤,編譯器無(wú)法確定List允許的是Fruit的具體哪一個(gè)子類(lèi)
19 }
20
21 public void test4(){
22 List<? super Apple> fruitList=new ArrayList<Fruit>();
23 fruitList.add(new Apple()); //編譯通過(guò),Apple和Apple的子類(lèi)都可以保證符合條件要求
24 fruitList.add(new Fruit()); //編譯錯(cuò)誤,編譯器無(wú)法確定List允許的是Apple的具體那個(gè)級(jí)別的父類(lèi)或接口
25 }
26 }
posted on 2012-08-22 03:17 王星游 閱讀(486) 評(píng)論(0) 編輯 收藏
只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。 | ||
![]() |
||
網(wǎng)站導(dǎo)航:
博客園
IT新聞
Chat2DB
C++博客
博問(wèn)
管理
|
||