泛型(Generic)
1. Jdk 5.0新加的特性
2. 在集合中使用泛型:
總結:
① 集合介面或集合類在jdk5.0時都修改為帶泛型的結構。
② 在例項化集合類時,可以指明具體的泛型型別
③ 指明完以後,在集合類或介面中凡是定義類或介面時,內部結構(比如:方法、構造器、屬性等)使用到類的泛型的位置,都指定為例項化的泛型型別。
比如:add(E e) --->例項化以後:add(Integer e)
④ 注意點:泛型的型別必須是類,不能是基本資料型別。需要用到基本資料型別的位置,拿包裝類替換
⑤ 如果例項化時,沒有指明泛型的型別。預設型別為java.lang.Object型別。
public class GenericTest { //在集合中使用泛型之前的情況: @Test public void test1(){ ArrayList list = new ArrayList(); //需求:存放學生的成績 list.add(78); list.add(76); list.add(89); list.add(88); //問題一:型別不安全 // list.add("Tom"); for(Object score : list){ //問題二:強轉時,可能出現ClassCastException int stuScore = (Integer) score; System.out.println(stuScore); } } //在集合中使用泛型的情況:以ArrayList為例 @Test public void test2(){ ArrayList<Integer> list = new ArrayList<Integer>(); list.add(78); list.add(87); list.add(99); list.add(65); //編譯時,就會進行型別檢查,保證資料的安全 // list.add("Tom"); //方式一: // for(Integer score : list){ // //避免了強轉操作 // int stuScore = score; // // System.out.println(stuScore); // // } //方式二: Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()){ int stuScore = iterator.next(); System.out.println(stuScore); } } //在集合中使用泛型的情況:以HashMap為例 @Test public void test3(){ // Map<String,Integer> map = new HashMap<String,Integer>(); //jdk7新特性:型別推斷 Map<String,Integer> map = new HashMap<>(); map.put("Tom",87); map.put("Jerry",87); map.put("Jack",67); // map.put(123,"ABC"); //泛型的巢狀 Set<Map.Entry<String,Integer>> entry = map.entrySet(); Iterator<Map.Entry<String, Integer>> iterator = entry.iterator(); while(iterator.hasNext()){ Map.Entry<String, Integer> e = iterator.next(); String key = e.getKey(); Integer value = e.getValue(); System.out.println(key + "----" + value); } } }
3.如何自定義泛型結構:泛型類、泛型介面;泛型方法。
a. 泛型類的構造器如下:public GenericClass(){}。錯誤的:public GenericClass<E>(){}
b. 在類/介面上宣告的泛型,在本類或本介面中即代表某種型別,可以作為非靜態屬性的型別、非靜態方法的引數型別、非靜態方法的返回值型別。但在靜態方法中不能使用類的泛型。
c. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];參考:ArrayList原始碼中宣告:Object[] elementData,而非泛型引數型別陣列。
4. 泛型方法:[訪問許可權] <泛型> 返回型別 方法名([泛型標識 引數名稱]) 丟擲的異常
public class DAO { public <E> E get(int id, E e) { E result = null; return result; } }
class Father<T1, T2> { } // 子類不保留父類的泛型 // 1)沒有型別 擦除 class Son1 extends Father {// 等價於class Son extends Father<Object,Object>{ } // 2)具體型別 class Son2 extends Father<Integer, String> { } // 子類保留父類的泛型 // 1)全部保留 class Son3<T1, T2> extends Father<T1, T2> { } // 2)部分保留 class Son4<T2> extends Father<Integer, T2> { } class Father<T1, T2> { } // 子類不保留父類的泛型 // 1)沒有型別 擦除 class Son<A, B> extends Father{//等價於class Son extends Father<Object,Object>{ } // 2)具體型別 class Son2<A, B> extends Father<Integer, String> { } // 子類保留父類的泛型 // 1)全部保留 class Son3<T1, T2, A, B> extends Father<T1, T2> { } // 2)部分保留 class Son4<T2, A, B> extends Father<Integer, T2> { } class Person<T> { // 使用T型別定義變數 private T info; // 使用T型別定義一般方法 public T getInfo() { return info; } public void setInfo(T info) { this.info = info; } // 使用T型別定義構造器 public Person() { } public Person(T info) { this.info = info; } // static的方法中不能宣告泛型 //public static void show(T t) { // //} // 不能在try-catch中使用泛型定義 //public void test() { //try { // //} catch (MyException<T> ex) { // //} //} }
//泛型方法:在方法中出現了泛型的結構,泛型引數與類的泛型引數沒有任何關係。 //換句話說,泛型方法所屬的類是不是泛型類都沒有關係。 //泛型方法,可以宣告為靜態的。原因:泛型引數是在呼叫方法時確定的。並非在例項化類時確定。 public static <E> List<E> copyFromArrayToList(E[] arr){ ArrayList<E> list = new ArrayList<>(); for(E e : arr){ list.add(e); } return list; } }
4. 泛型在繼承方面的體現
雖然類A是類B的父類,但是G<A> 和G<B>二者不具備子父類關係,二者是並列關係。
類A是類B的父類,A<G> 是 B<G> 的父類
@Test public void test1(){ Object obj = null; String str = null; obj = str; Object[] arr1 = null; String[] arr2 = null; arr1 = arr2; //編譯不通過 // Date date = new Date(); // str = date; List<Object> list1 = null; List<String> list2 = new ArrayList<String>(); //此時的list1和list2的型別不具有子父類關係 //編譯不通過 // list1 = list2; /* 反證法: 假設list1 = list2; list1.add(123);導致混入非String的資料。出錯。 */ show(list1); show1(list2); } public void show1(List<String> list){ } public void show(List<Object> list){ } @Test public void test2(){ AbstractList<String> list1 = null; List<String> list2 = null; ArrayList<String> list3 = null; list1 = list3; list2 = list3; List<String> list4 = new ArrayList<>(); }
5. 萬用字元:?
/* 2. 萬用字元的使用 萬用字元:? 類A是類B的父類,G<A>和G<B>是沒有關係的,二者共同的父類是:G<?> */ @Test public void test3(){ List<Object> list1 = null; List<String> list2 = null; List<?> list = null; list = list1; list = list2; //編譯通過 // print(list1); // print(list2); // List<String> list3 = new ArrayList<>(); list3.add("AA"); list3.add("BB"); list3.add("CC"); list = list3; //新增(寫入):對於List<?>就不能向其內部新增資料。 //除了新增null之外。 // list.add("DD"); // list.add('?'); list.add(null); //獲取(讀取):允許讀取資料,讀取的資料型別為Object。 Object o = list.get(0); System.out.println(o); } public void print(List<?> list){ Iterator<?> iterator = list.iterator(); while(iterator.hasNext()){ Object obj = iterator.next(); System.out.println(obj); } } /* 3.有限制條件的萬用字元的使用。 ? extends A: G<? extends A> 可以作為G<A>和G<B>的父類,其中B是A的子類 ? super A: G<? super A> 可以作為G<A>和G<B>的父類,其中B是A的父類 */ @Test public void test4(){ List<? extends Person> list1 = null; List<? super Person> list2 = null; List<Student> list3 = new ArrayList<Student>(); List<Person> list4 = new ArrayList<Person>(); List<Object> list5 = new ArrayList<Object>(); list1 = list3; list1 = list4; // list1 = list5; // list2 = list3; list2 = list4; list2 = list5; //讀取資料: list1 = list3; Person p = list1.get(0); //編譯不通過 //Student s = list1.get(0); list2 = list4; Object obj = list2.get(0); ////編譯不通過 // Person obj = list2.get(0); //寫入資料: //編譯不通過 // list1.add(new Student()); //編譯通過 list2.add(new Person()); list2.add(new Student()); } }
1. 萬用字元不能用在泛型方法的宣告上,返回值前面<>不能使用
public static <?> void test(ArrayList<?> list){ }
2. 萬用字元不能使用在泛型類的宣告上
class GenericTypeClass<?>{ }
3. 萬用字元不能用在建立物件上,右邊屬於建立集合物件
ArrayList<?> list2 = new ArrayList<?>();