本文為從網上東拼西湊的java泛型學習筆記,摘出了我認為有價值的部分,尚未整理,先放在這裡備份。
http://docs.oracle.com/javase/1.5.0/docs/guide/language/generics.html
泛型在類、介面和方法中定義,在實現(extends,implements)和執行個體化時使用。
定義類或介面時,使用"<E extends Fruit>"這種形式,之後就可以在類中對E進行操作。
定義方法所接收的參數時,使用"List<? extends Fruit>"這種形式,就可以接收這個範圍的List做參數。
執行個體化時,不能使用問號這種形式來指定泛型——不能new List<? extends Apple>();
繼承或實現時,也不能使用問號這種形式來指定泛型——不能public interface MyList extends List<? extends Apple>
關於extends和super關鍵字的PECS(producer-extends, consumer-super)原則:
如果參數化型別表示一個T生產者,就使用<? extends T>,因為它只能get,用於將資料從生產者取出(只要生產者可以生產T的子類,那就一定可以生產T);
如果參數化型別表示一個T消費者,就使用<? super T>,因為它只能add,用於將資料add入消費者(只要消費者可以消費T的超類,那就一定可以消費T)。
http://www.infoq.com/cn/articles/cf-java-generics
泛型的繼承原則:
1,相同型別參數的泛型類的關係取決於泛型類自身的繼承體繫結構。即List<String>是Collection<String> 的子類型,List<String>可以替換Collection<String>。這種情況也適用於帶有上下界的型別宣告。
2,當泛型類的型別宣告中使用了萬用字元的時候, 其子類型可以在兩個維度上分別展開。如對Collection<? extends Number>來說,其子類型可以在Collection這個維度上展開,即List<? extends Number>和Set<? extends Number>等;也可以在Number這個層次上展開,即Collection<Double>和 Collection<Integer>等。如此迴圈下去,ArrayList<Long>和 HashSet<Double>等也都算是Collection<? extends Number>的子類型。
3,如果泛型類中包含多個型別參數,則對於每個型別參數分別應用上面的規則。
泛型的定義原則:
1,泛型類與一般的Java類基本相同,只是在類和介面定義上多出來了用<>聲明的型別參數。
2,一個類可以有多個型別參數,如 MyClass<X, Y, Z>。
3,每個型別參數在聲明的時候可以指定上界。
4,所聲明的型別參數在Java類中可以像一般的類型一樣作為方法的參數和傳回值,或是作為域和局部變數的類型。
5,由於類型擦除機制,型別參數並不能用來建立對象或是作為靜態變數的類型。
http://www.ibm.com/developerworks/cn/java/j-jtp01255.html
如果沒有向後相容性顧慮,那麼Collection架構會這麼設計(而實際上不是):
interface Collection<E> {
public T[] toArray(Class<T super E> elementClass);
}
這樣很通用,假設E是Apple類,那麼傳入任何一個Apple的父類(例如Fruit)都合法,會返回Fruit數組。
如果傳入Object,那就相當於現在Collection介面的Object[] toArray()方法了。
同樣,如果沒有向後相容性顧慮,那麼Collection架構的remove和removeAll會這麼寫(而實際上不是):
interface Collection<E> {
public boolean remove(E e); // not really
public void removeAll(Collection<? extends E> c); // not really
}
缺陷是:只能傳入當前泛型的相容類型,不能傳入任意Object或Collection<Object>——這樣無法到相容之前的API定義。
不能建立泛型型別的對象,因為編譯器不知道要調用什麼建構函式。如果泛型類需要構造用泛型型別參數來指定類型的對象,那麼建構函式應該接受類文字(Foo.class)並將它們儲存起來,以便通過反射建立執行個體(類似於上述Collection的toArray方法)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/20/2228938.html
型別安全的異構容器(內部用Map<Class<?>,Object>來實現):
public class Favorites {
public <T> void putFavorite(Class<T> type,T instance);
public <T> T getFavorite(Class<T> type);
}
例如用於資料庫不定數目的columns:每個cell的資料都以這種方式指定類型儲存Favorites到容器中,再將此容器當作元素儲存到更上層的結果容器中。
不能執行個體化泛型型別的數組:new List<String>[3]是不合法的。
但是在可以確定類型的情況下,將Object數組強制轉換成泛型數組是合法的,例如ArrayList的源碼:return (T[]) Arrays.copyOf(elementData, size, a.getClass());
泛型方法:
聲明方法時將用到的型別參數列在前面即可(所在類上不需要定義此參數類型):
public static <E> Set<E> union(Set<E> s1,Set<E> s2) ;
靜態工具方法特別適合這樣寫。
http://blog.csdn.net/mai0net/article/details/7320399
一般來說編寫java泛型方法時,傳回值類型和至少一個參數類型應該是泛型,而且類型應該是一致的,如果只有傳回值類型或參數類型之一使用了泛型,這個泛型方法的使用就大大的限制了,基本限制到跟不用泛型一樣的程度。
第一種:public static <T extends CommonService> T getService(Class<T> clazz);
NoticeService noticeService=CommonService.getService(NoticeService.class);//正確的使用第一種泛型方法,不會出現編譯錯誤。
NoticeService noticeService=CommonService.getService(UserService.class);//不正確的使用第一種泛型方法,會出現編譯錯誤。
第二種:public static <T> T getService(Class<? extends CommonService> clazz);
NoticeService noticeService=CommonService.getService(NoticeService.class);//正確的使用第二種泛型方法,不會出現編譯錯誤,邏輯也正確,運行時不會出現異常。
NoticeService noticeService=CommonService.getService(UserService.class);//不正確的使用第二種泛型方法,不會出現編譯錯誤,但邏輯不正確,運行時會出現異常,危險!
網上不少關於泛型的說明很好,可是不知道是不是jdk版本問題,居然沒辦法實際通過編譯,於是我只好自己在jdk1.7下一一重寫和嘗試,最後加上總結和注釋,如下:
public class Fruit{}
public class Apple extends Fruit {}
public class Basket{//從泛型化的list中取出Fruit,裡面的元素一定是Fruit的子類,所以都可以當作Fruit返回public static Fruit getFruit(List<? extends Fruit> list){return list.get(0);}//將Apple放入泛型化的list(這個list可以接收的類型是Apple的父類,所以一定可以接收apple或apple的子類)public static void addApple(List<? super Apple> list,Apple apple){list.add(apple);}}
public class GenericTester extends AbstractTester{public void test1(){List<Apple> appleList = new ArrayList<Apple>();appleList.add(new Apple()); //先定義List<Apple>,填入元素Fruit fruit=Basket.getFruit(appleList); //List<Apple>類型符合List<? extends Fruit>的要求}public void test2(){List<Fruit> fruitList=new ArrayList<Fruit>();Basket.addApple(fruitList, new Apple()); //List<Fruit>符合List<? super Apple>的要求}}
public class GenericCompileTester {public void addInteger1(List<?> list){list.add(1); //編譯錯誤,因為問號代表“編譯時間不確定,但運行時確定”的類型,Integer不一定符合要求,其實這裡相當於<? extends Object>}public void addInteger2(List<Object> list){list.add(1); //編譯通過,因為編譯時間和運行時都確定了是Object類型,Integer肯定符合要求}public void test(){List<String> list=new ArrayList<String>();addInteger2(list); //編譯錯誤,因為List<String>不能當作List<Object>傳入,否則數字1將被當作String添加,類型將不再安全}public void test3(){List<? extends Fruit> fruitList=new ArrayList<Apple>();fruitList.add(new Apple());//編譯錯誤,編譯器無法確定List允許的是Fruit的具體哪一個子類fruitList.add(new Fruit());//編譯錯誤,編譯器無法確定List允許的是Fruit的具體哪一個子類}public void test4(){List<? super Apple> fruitList=new ArrayList<Fruit>();fruitList.add(new Apple());//編譯通過,Apple和Apple的子類都可以保證符合條件要求fruitList.add(new Fruit());//編譯錯誤,編譯器無法確定List允許的是Apple的具體那個層級的父類或介面}}