天天看點

泛型通配符、定義泛型類型

一、泛型的通配符

1、

如果要定義一個方法,用于列印出任何參數化類型的集合中的所有資料,該如何定義呢?上一篇提到過了,參數化類型不考慮參數類型

間的繼承關系。

是以如下定義就通不過編譯:

public static void printCollection(Collection<Object> cols) {

    for(Object obj:cols) {

        System.out.println(obj);

    }

}

正确的方式應把Object替換為?通配符:

public static void printCollection(Collection<?> cols) {

    for(Object obj:cols) {

        System.out.println(obj);

    }

    //cols.add("abc");// 錯誤,因為?無法确定是否将來比對的是String

    cols.size();// 沒錯,此方法與參數類型無關

    //cols = new HashSet<Date>();

}

是以,?通配符可以引用其他各種參數化的類型,?通配符定義的變量主要用作引用,可以調用與參數化無關的方法,不能調用與參數化

有關的方法(比如add)

泛型中的?通配符的擴充

1)限定通配符的上邊界:

正确:Vector<? extends Number> x = new Vector<Integer>();

錯誤:Vector<? extends Number> x = new Vector<String>();

2)限定通配符的下邊界:

正确:Vector<? super Integer> x = new Vector<Number>();

錯誤:Vector<? super Integer> x = new Vector<Byte>();

而且限定通配符總是包括自己。

2、泛型綜合應用的展現

HashMap<String, Integer> maps=new HashMap<String, Integer>();

maps.put("sss", 24);

maps.put("ddd", 27);

maps.put("aaa", 25);

//Map類未實作Itretor接口,是以不能直接疊代,但可以用entrySet()方法擷取裡面所有元素的集合

Set<Map.Entry<String, Integer>> entrySet = maps.entrySet();

//Map.Entry<K,V>是Map的一個嵌套類。它有getKey和getValue方法,相當于一個JavaBean。

//疊代取出Set集合中的鍵值對。

for(Map.Entry<String, Integer> entry:entrySet) {

    System.out.println(entry.getKey() + "," + entry.getValue());

}

另外,jsp頁面中也經常要對Set或Map集合進行疊代:

<c:forEach items="${map}" var="entry">

  ${entry.key}:${entry.value}

</c:forEach>

3、自定義一個泛型方法

// 自定義一個類型的時候,要在傳回值的前面加上<T>

// 定義一個泛型方法,可以調換某個類型數組的兩個元素位置

//隻有引用類型才可以作為泛型方法的實際參數,基本類型編譯時不會通過

private static <T> void swap(T[] a, int i, int j) {

T tmp = a[i];

a[i] = a[j];

a[j] = tmp;

}

那麼在調用方法時:

swap(new String[]{"abc","xxx","ddd"}, 1, 2);

swap(new int[]{1,2,3,4,5}, 2, 4);

上面兩句第一句沒錯,第二句會報錯。

除了在應用泛型時可以使用extends限定符,在定義泛型時也可以使用extends限定符,例如Class.getAnnotation()方法的定義。并且

可以用&來指定多個邊界,如<V extends Serializable&clonable> void method(){}

普通方法、構造方法和靜态方法中都可以使用泛型。

也可以用類型變量表示異常,成為參數化的異常,可用于方法的throws清單中,但是不能用于catch子句中。

在泛型中可以同時又多個類型參數,在定義他們的尖括号中用逗号分隔,例如:

public static <K,V> V getValue(K key) {return map.get(key);}

二、定義泛型類型

1、

如果類的執行個體對象中的多處都要用到同一個泛型參數,即這些地方引用的泛型類型要保持同一個實際類型時,這時候就要采用泛型類型

的方式進行定義,也就是類級别的泛型,文法格式如下:

public Class GenericDao<T> {

    private T field1;

    public void save(T obj) {}

    public T getById(int id) {}

}

類級别的泛型是根據引用該類名時指定的類型資訊來參數化類型變量的,例如,如下兩種方式都可以:

GenericDao<String> dao = null;

new GenericDao<String>();

注意:

在對泛型類型進行參數化時,類型參數的執行個體必須是引用類型,不能是基本類型。

當一個變量被聲明為泛型時,隻能被執行個體變量和方法調用(還有内嵌類型),而不能被靜态變量和靜态方法調用。因為靜态成員時被所

有參數化的類所共享的,是以靜态成員不應該有類級别的類型參數。

當類中隻有一個方法需要使用泛型時,使用的是類級别的泛型。

2、

假如有這麼一個方法:

public static void applyVector(Vector<Date> v) {

}

//那麼如何通過一個泛型集合的變量,擷取這個類型參數到底是什麼類型呢?下面的方式顯然不行。

Vector<Date> v = new Vector<Date>();

//下面的方法變量進行定義時,getMethod的第二個參數,即參數類型,應該就是Vector.class

//而不是Vector<Date>.class,因為在編譯完了之後,集合變量已經去類型化了

//同時,如果同時定義兩個applyVector方法,分别為:

//applyVector(Vector<Date> v)

//applyVector(Vector<Integer> v)

//編譯器會報錯,因為它會把這兩個方法看做是同一個。(去類型化的原因)

Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);

Type[] types = applyMethod.getGenericParameterTypes();//擷取該方法的參數類型數組

ParameterizedType pType = (ParameterizedType)types[0];//擷取參數類型數組的第一個值

System.out.println(pType.getRawType());

System.out.println(pType.getActualTypeArguments()[0]);//知道參數隻有一個,直接取第一位的值