型別通配符
型別通配符一般是使用?來取代具體的型別實參(這裡是型別實參,而不是型別字參)。當操作類型時不需要使用類型的特定功能時,只使用Object類別中的功能,那麼可以用 ? 通配符來表未知類型。例如 List> 在邏輯上是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中泛型通配符和有界的型別參數的詳細內容。更多資訊請關注PHP中文網其他相關文章!