類型萬用字元
類型萬用字元一般是使用?代替具體的類型實參(此處是類型實參,而不是類型形參)。當操作類型時不需要使用類型的具體功能時,只使用Object類中的功能,那麼可以用 ? 萬用字元來表未知類型。例如 List<?> 在邏輯上是List<String>、List<Integer> 等所有List<具體類型實參>的父類。
public class GenericTest { public static void main(String[] args) { List<String> name = new ArrayList<String>(); List<Integer> age = new ArrayList<Integer>(); List<Number> number = new ArrayList<Number>(); name.add("zwj"); age.add(18); number.add(120); getNumberData(age); // 報錯 getNumberData(number); // 120 getData(name); // zwj getData(age); // 18 getData(number); // 120 //getUperNumber(name); // 出現錯誤,方法中的參數已經限定了參數泛型上限為Number getUperNumber(age); //18 getUperNumber(number); //120 } /** * 在使用List<Number>作為形參的方法中,不能使用List<Ingeter>的執行個體傳入, * 也就是說不能把List<Integer>看作為List<Number>的子類; */ public static void getNumberData(List<Number> data) { System.out.println("data :" + data.get(0)); } /** * 使用類型萬用字元可以表示同時是List<Integer>和List<Number>的參考型別。 * 類型萬用字元一般是使用?代替具體的類型實參,注意此處是類型實參; * 和Number、String、Integer一樣都是一種實際的類型,可以把?看成所有類型的父類。 */ public static void getData(List<?> data) { System.out.println("data :" + data.get(0)); } /** * 類型萬用字元上限通過形如List來定義,如此定義就是萬用字元泛型值接受Number及其下層子類類型。 */ public static void getUperNumber(List<? extends Number> data) { System.out.println("data :" + data.get(0)); }}
有界的型別參數
有的時候我們會想限制那些被允許傳遞到一個型別參數的類型種類範圍。例如,一個運算元字的方法可能只希望接受Number或者Number子類的執行個體。這時猴就需要為泛型添加上邊界,即傳入的類型實參必須是指定類型的子類型。
要聲明一個有界的型別參數,首先列出型別參數的名稱,後跟extends或super關鍵字,最後緊跟它的上界或下界。由此可以知道泛型的上下邊界的添加必須與泛型的聲明在一起 。
<? extends T> 表示該萬用字元所代表的類型是T類型的子類。 例如往集合中添加元素時,既可以添加E類型對象,又可以添加E的子類型對象。為什嗎?因為取的時候,E類型既可以接收E類對象,又可以接收E的子類型對象。
<? super T> 表示該萬用字元所代表的類型是T類型的父類。例如當從集合中擷取元素進行操作的時候,可以用當前元素的類型接收,也可以用當前元素的父類型接收。
public class GenericMethodTest { // 比較三個值並返回最大值 public static <T extends Comparable<T>> T getMaxNuum(T x, T y, T z) { T max = x; // 假設x是初始最大值 if ( y.compareTo( max ) > 0 ){ max = y; //y 更大 } if ( z.compareTo( max ) > 0 ){ max = z; // 現在 z 更大 } return max; // 返回最大對象 } public static void main( String args[] ) { System.out.println( "結果 " + getMaxNuum(3, 4, 5) ); // 結果 5 System.out.println( "結果 " + getMaxNuum(1.2, 6.6, 10.10) ); // 結果 10.10 }}
我們也可以把之前的泛型類的定義改一下:
public class GenericClassDemo<T extends Number> { private T t; public GenericClassDemo() { } public GenericClassDemo(T t) { this.t = t; } public void setT(T t) { this.t = t; } public T getT(){ return t; }}
此時在執行個體化GenericClassDemo泛型類時參數類型只能是Number和Number的子類。在此基礎上我們再來看一個泛型方法的例子:
/** * 在泛型方法中添加上下邊界限制的時候, 必須在泛型聲明的時候添加; * 也就是在許可權修飾符與傳回值之間的<T>上添加上下邊界 */public <T extends Number> T getT(GeneriClassDemo<T> demo){ T t = demo.getT(); return t;}
泛型數組
在java中是不能建立一個確切的泛型型別的數組的。
List<String>[] lsa = new ArrayList<String>[10]; // Not really allowed. Object o = lsa; Object[] oa = (Object[]) o; List<Integer> li = new ArrayList<Integer>(); li.add(new Integer(3)); oa[1] = li; // Unsound, but passes run time store check String s = lsa[1].get(0); // Run-time error: ClassCastException.
這種情況下,由於JVM泛型的擦除機制,在運行時JVM是不知道泛型資訊的,所以可以給oa[1]賦上一個ArrayList而不會出現異常,但是在取出資料的時候卻要做一次類型轉換,所以就會出現ClassCastException,如果可以進行泛型數組的聲明,上面說的這種情況在編譯期將不會出現任何的警告和錯誤,只有在運行時才會出錯。而對泛型數組的聲明進行限制,對於這樣的情況,可以在編譯期提示代碼有型別安全問題,比沒有任何提示要強很多。
下面採用萬用字元的方式是被允許的:數組的類型不可以是類型變數,除非是採用萬用字元的方式,因為對於萬用字元的方式,最後取出資料是要做顯式的類型轉換的。
List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type. Object o = lsa; Object[] oa = (Object[]) o; List<Integer> li = new ArrayList<Integer>(); li.add(new Integer(3)); oa[1] = li; // Correct. Integer i = (Integer) lsa[1].get(0); // OK
相關文章:
java 泛型中 T 和 問號(萬用字元)的區別
Java中的泛型詳解