1,集合和泛型
在沒有給集合制定類型時,一個集合可以加入任何類型的資料,但是規定了類型之後,則只能放同一種類型的資料
2,泛型是jdk1.5的新特性
3,去類型化(泛型基本可以說是通過編譯器實現的,在運行階段類型會被擦除)
ArrayList<String> lista = new ArrayList<String>();
ArrayList<Integer> listb = new ArrayList<Integer>();
if(lista.getClass() == listb.getClass()){
System.out.println(true
);
啟動並執行時候去類型化,編譯完的位元組碼是同一個,裡面已經沒有泛型資訊,
所以,泛型是給編譯器看的,如果繞過編譯器,直接操作位元組碼,泛型的作用就消失了,比如下面的例子
在一個ArrayList<Integer>裡面不能直接添加一個String,因為這個時候編譯器會報錯,而如果用反射,則可以實現
ArrayList<String> lista = new ArrayList<String>();lista.add("abc");//下面一句則會出錯,被編譯器檢查出與泛型資訊不匹配 //lista.add(1); lista.getClass().getMethod("add", Object.class).invoke(lista, 1);System.out.println(lista);
4,關於泛型的轉化相容關係
看下面代碼,大致就能理解常見的轉化關係
//原始類型可以 和執行個體化了的泛型型別相互賦值ArrayList<String> lista = new ArrayList();ArrayList listb = new ArrayList<String>();//參數類型不考慮參數的繼承關係,無論是向下轉型還是向上轉型//ArrayList<String> listc = new ArrayList<Object>();//ArrayList<Object> listd = new ArrayList<String>();//但是,注意,這樣則無錯ArrayList liste = new ArrayList<String>();ArrayList<Object> listf = liste;//不能建立數組元素為參數類型的數組//ArrayList<String>[] array = new ArrayList<String>[10];
5,問號萬用字元 ?
作用:問號萬用字元可以指向各種參數化的參考型別,萬用字元定義的變數,可以調用與參數化無關的方法(和泛型無關),但是不可以調用和參數化有關的方法
下面是一個輸出任何類型集合類的一個方法
public static void PrintAllCollection(Collection<?> collection){//不可以調用和參數化有關的方法,但是可以調用和參數化無關的方法//collection.add(1); //errorcollection.size();for(Object obj:collection){System.out.println(obj);}//下面一行也是正確的,相當於調用桉樹時的轉化collection = new ArrayList<String>();}
6,萬用字元的擴充
限定萬用字元的上邊界
Vector<? extends Number> vec = new Vector<Integer>(); // right
Vector<? extends String> ved = new Vector《Float>(); // error
限定萬用字元的下邊界
Vector<? super Integer> vee = new Vector<Number>(); // right
Vector<? super String> vef = new Vector《Float>(); // error
提示
限定萬用字元包括自己
關於向上向下轉型請看這篇簡明扼要的文章 java泛型中的上界下界(上限下限)
下面是關於上界下界的另一個例子
關於?extends
List<Apple> apples = new ArrayList<Apple>();List<? extends Fruit> fruits = apples;//error--fruits.add(new Strawberry());//error--fruits.add(new Apple());//error--fruits.add(new Fruit());Fruit get = fruits.get(0);//原因/* * 你可以這樣想:這個? extends T 萬用字元告訴編譯器我們在處理一個類型T的子類型, * 但我們不知道這個子類型究竟是什麼。因為沒法確定,為了保證型別安全, * 我們就不允許往裡面加入任何這種類型的資料。另一方面,因為我們知道,不論它是什麼類型, * 它總是類型T的子類型,當我們在讀取資料時,能確保得到的資料是一個T類型的執行個體: */
關於? super
List<Fruit> fruits2 = new ArrayList<Fruit>();// error----List<? super Apple> = fruits2;//我們看到fruits指向的是一個裝有Apple的某種超類(supertype)的List。//同樣的,我們不知道究竟是什麼超類,但我們知道Apple和任何Apple的子類都跟它的類型相容。//既然這個未知的類型即是Apple,也是GreenApple的超類,我們就可以寫入:fruits2.add(new Apple());fruits2.add(new GreenApple());//如果我們想往裡面加入Apple的超類,編譯器就會警告你://error --- fruits2.add(new Fruit());//error --- fruits2.add(new Object());//因為我們不知道它是怎樣的超類,所有這樣的執行個體就不允許加入
總結 ? extends 和 the ? super 萬用字元的特徵,我們可以得出以下結論:
- 如果你想從一個資料類型裡擷取資料,使用 ? extends 萬用字元
- 如果你想把對象寫入一個資料結構裡,使用 ? super 萬用字元
- 如果你既想存,又想取,那就別用萬用字元。
7,類型的推斷
如下代碼,可以解釋類型的推斷的基本問題
調用
//泛型型別為兩個參數的交集,即向上距離最近的相同父類int a = TypeDeduce(1,2);Number b = TypeDeduce(1,2.2);Object c = TypeDeduce(1,"aaa");
函數
//泛型型別的推斷//泛型型別為兩個參數的交集,即向上距離最近的相同父類public static <T> T TypeDeduce(T a, T b){return null;}
8,泛型的實際類型不能是基本類型,而必須為參考型別
//swap(new int[]{2,4,5},1,2);swap(new Integer[]{2,4,5},1,2);//泛型型別的推斷//泛型型別為兩個參數的交集,即向上距離最近的相同父類public static <T> T TypeDeduce(T a, T b){return null;}
9,泛型類
class GenericDemo <T>{ public void delete(T obj){ } public void add(T obj){ } public T find(T obj) return null; }
//error,,類中靜態方法不能用類層級的靜態方法,但是可以用方法層級的靜態方法
// public static void function1(T obj){
//}
//類中可以用方法層級的靜態方法 public static <E> void function21(E obj){
}
}
其他一些關於Java 泛型的學習博文
Java泛型簡明教程