1回顧泛型類
泛型類:具有一個或多個泛型變量的類被稱之為泛型類。
class ClassGenericity<T> { //在類里面可以直接使用T的類型 T aa; public void test11(T bb) { //................ } //靜態方法 在類上面定義的泛型,不能再靜態方法里面使用 public static <A> void test12(A cc) { //.................. } } public class TestClassGenericity{ public static void main(String[] args) { ClassGenericity<String>genericity=new ClassGenericity<String>(); genericity.test11("1234"); ClassGenericity.test12(6789); } }
2 泛型方法
泛型方法的特點:
方法的參數中可以使用泛型變量;
方法的返回值中可以使用泛型變量。
public class MethodGenericity { public static void main(String[] args) { String arr[]={"aaa","bbb","ccc","ddd"}; System.out.println(Arrays.toString(arr)); exchange(arr,0,3); //交換0,3位置元素 System.out.println(Arrays.toString(arr)); } private static<T> void exchange(T[] arr, int i, int j) { T temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; } }
public class Test1 { public <T> T get(T[] ts) { return ts[ts.length / 2]; } @Test public void test() { String[] names ={"zhangSan", "liSi","wangWu"}; System.out.println(get(names));//輸出lisi } }
調用泛型方法時無需指定泛型變量,編譯器會通過實際參數的類型來識別泛型變量的類型,上例中傳遞的參數為String[]類型,那么相當於給泛型類型T賦值為String。
3 繼承(實現)泛型類(接口)
繼承泛型類需要為父類的泛型變量賦值!就好比創建泛型類的對象時需要給泛型變量賦值一樣。
創建泛型類對象:List<String> list = new ArrayList<String>();
繼承泛型類1(子類也是泛型類):public class MyList1<T> extends ArrayList<T> {...}
繼承泛型類2(子類不是泛型類):public class MyList2 extends ArrayList<String> {...}
4 通配符
為了說明通配符的作用,我們先看個例子:
List<Object> list1 = new ArrayList<String>();
List<Object> list2 = new ArrayList<Integer>();
上面的調用都是編譯不通過的!這說明想寫一個即可以打印list1,又可以打印list2的方法是不可能的!
public static void fun(List<Object> list) {…} List<String> list1 = new ArrayList<String>(); List<Integer> list2 = new ArrayList<Integer>(); fun(list1);//編譯不通過 fun(list2);//編譯不通過
如果把fun()方法的泛型參數去除,那么就OK了。即不使用泛型!
public static void fun(List list) {…}//會有一個警告 List<String> list1 = new ArrayList<String>(); List<Integer> list2 = new ArrayList<Integer>(); fun(list1); fun(list2);
上面代碼是沒有錯了,但會有一個警告。警告的原因是你沒有使用泛型!Java希望大家都去使用泛型。你可能會說,這里根本就不能使用泛型!!!!!
4.1 通配符概述
通配符就是專門處理這一問題的。
public static void fun(List<?> list) {…}
上面代碼中的“?”就是一個通配符,它只能在“<>”中使用。這時你可以向fun()方法傳遞List<String>、List<Integer>類型的參數了。當傳遞List<String>類型的參數時,表示給“?”賦值為String;當傳遞List<Integer>類型的參數給fun()方法時,表示給“?”賦值為Integer。
4.2 通配符的缺點
帶有通配符的參數不能使用與泛型相關的方法,例如:list.add(“hello”)編譯不通過。
上面的問題是處理了,但通配符也有它的缺點。在上面例子中,List<?> list參數中的通配符可以被賦任何值,但同時你也不知道通配符被賦了什么值。當你不知道“?”是什么時,會使你不能使用任何與泛型相關的方法。也就是說fun()方法的參數list不能再使用它的與泛型相關的方法了。例如:list.add(“hello”)是錯誤的,因為List類的add()方法的參數是T類型,而現在你不知道T是什么類型,你怎么去添加String的東西給list呢?如果使用者在調用fun()方法時傳遞的不是List<String>,而是List<Integer>時,你添加String當然是不可以的。當然,還可以調用list的get()方法。就算你不知道“?”是什么類型,但它肯定是Object類型的。所以你可以:Object o = list.get(0);
4.3 通配符的限制
通配符只能出現在引用的定義中,而不能出現在創建對象中。例如:new ArrayList<?>(),這是不可以的。ArrayList<?> list = null,這是可以的。
4.4 帶有下邊界的通配符
List<? extends Number> list;
其中<? extends Number>表示通配符的下邊界,即“?”只能被賦值為Number或其子類型。
public static void fun(List<? extends Number> list) {…} fun(new ArrayList<Integer>());//ok fun(new ArrayList<Double>());//ok fun(new ArrayList<String>());//不ok
當fun()方法的參數為List<? extends Number>后,說明你只能賦值給“?”Number或Number的子類型。雖然這多了一個限制,但也有好處,因為你可以用list的get()方法。就算你不知道“?”是什么類型,但你知道它一定是Number或Number的子類型。所以:Number num = list.get(0)是可以的。但是,還是不能調用list.add()方法!
4.5 帶有下邊界的通配符
List<? super Integer> list;
其中<? super Integer>表示通配符的下邊界,即“?”只能被賦值為Integer或其父類型。
public static void fun(List<? super Integer> list) {…} fun(new ArrayList<Integer>());//ok fun(new ArrayList<Number>());//ok fun(new ArrayList<Object>());//ok fun(new ArrayList<String>());//不ok
這時再去調用list.get()方法還是只能使用Object類型來接收:Object o = list.get(0)。因為你不知道“?”到底是Integer的哪個父類。但是你可以調用list.add()方法了,例如:list.add(new Integer(100))是正確的。因為無論“?”是Integer、Number、Object,list.add(new Integer(100))都是正確的。
4.6 通配符小結
1. 方法參數帶有通配符會更加通用;
2. 帶有通配符類型的對象,被限制了與泛型相關方法的使用;
3. 下邊界通配符:可以使用返回值為泛型變量的方法;
4. 上邊界通配符:可以使用參數為泛型變量的方法。
4.7 通配符應用實例
boolean addAll(Collection<? extends E> c)//JDK集合的addAll方法
List<Number> numList = new ArrayList<Number>(); List<Integer> intList = new ArrayList<Integer>(); numList.addAll(intList);//正確!!!!!!addAll(Collection<? extends Number> c), 傳遞的是List<Integer>
如果用改成boolean addAll(Collection<E> c)
List<Number> numList = new ArrayList<Number>(); List<Integer> intList = new ArrayList<Integer>(); numList.addAll(intList);//錯誤!!!!!addAll(Collection<Number> c), 傳遞的是List<Integer>
5 通配符應用實例
import java.util.ArrayList; import java.util.List; public class Demo2 { public void fun1() { Object[] objArray = new String[10];//正確!!! objArray[0] = new Integer(100);//錯誤!!!編譯器不會報錯,但是運行時會拋ArrayStoreException //List<Object> objList = new ArrayList<String>();//錯誤!!!編譯器報錯,泛型引用和創建兩端,給出的泛型變量必須相同! } public void fun2() { List<Integer> integerList = new ArrayList<Integer>(); print(integerList); List<String> stringList = new ArrayList<String>(); print(stringList); } /* * 其中的?就是通配符,?它表示一個不確定的類型,它的值會在調用時確定下來 * 通配符只能出現在左邊!即不能在new時使用通配符!!! * List<?> list = new ArrayList<String>(); * 通配符好處:可以使泛型類型更加通用!尤其是在方法調用時形參使用通配符! */ public void print(List<?> list) { //list.add("hello");//錯誤!!!編譯器報錯,當使用通配符時,對泛型類中的參數為泛型的方法起到了副作用,不能再使用! Object s = list.get(0);//正確!!!但是只是得益於object類是所有類的父類,換成其他任何類編譯器都會報錯!說明當使用通配符時,泛型類中返回值為泛型的方法,也作廢了! } public void fun3() { List<Integer> intList = new ArrayList<Integer>(); print1(intList); List<Long> longList = new ArrayList<Long>(); print1(longList); } /* * 給通配符添加了限定: * 只能傳遞Number或其子類型 * 子類通配符對通用性產生了影響,但使用形參更加靈活 */ public void print1(List<? extends Number> list) { //list.add(new Integer(100));//錯誤!!!編譯器報錯,說明參數為泛型的方法還是不能使用(因為?也可能為Long型) Number number = list.get(0);//正確!!!返回值為泛型的方法可用了! } public void fun4() { List<Integer> intList = new ArrayList<Integer>(); print2(intList); List<Number> numberList = new ArrayList<Number>(); print2(numberList); List<Object> objList = new ArrayList<Object>(); print2(objList); } /* * 給通配符添加了限定 * 只能傳遞Integer類型,或其父類型 */ public void print2(List<? super Integer> list) { list.add(new Integer(100));//正確!!!參數為泛型的方法可以使用了 Object obj = list.get(0);//正確!!!但是只是得益於object類是所有類的父類,換成其他任何類編譯器都會報錯!說明返回值為泛型的方法,還是不能使用 } }
6 泛型父類獲取子類傳遞的類型參數
import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import org.junit.Test; public class Demo1 { @Test public void fun1() { new B();//輸出:class B java.lang.String } @Test public void fun2() { new C();//輸出:class C java.lang.Integer } } class A<T> { public A() { //在這里獲取子類傳遞的泛型信息,要得到一個Class! Class clazz = this.getClass(); //得到子類的類型 System.out.print(clazz+" "); Type type = clazz.getGenericSuperclass(); //獲取傳遞給父類參數化類型 ParameterizedType pType = (ParameterizedType) type; //它就是A<String> Type[] types = pType.getActualTypeArguments(); //它就是一個Class數組 Class c = (Class)types[0]; //它就是String或者Integer //連成一句:Class c = (Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; System.out.println(c.getName()); } } class B extends A<String> {} class C extends A<Integer> {}