泛型之中的通配符(Wildcards)使用
限制泛型可用類型
在定義泛型類別時,預設可以使用任何的類型來實例化泛型類型中的類型。
但是如果想限制使用泛型類別時,只能用某個特定類型或者是其子類型才能實例化該類型時,可以在定義類型時,使用extends關鍵字指定這個類型必須是繼承某個類,或者實現某個接口,也可以是這個類或接口本身。
比如下面的例子:
import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; public class ListGenericFoo<T extends List> { private T[] fooArray; public T[] getFooArray() { return fooArray; } public void setFooArray(T[] fooArray) { this.fooArray = fooArray; } public static void main(String[] args) { ListGenericFoo<LinkedList> foo1 = new ListGenericFoo<LinkedList>(); ListGenericFoo<ArrayList> foo2 = new ListGenericFoo<ArrayList>(); //Error: Bound mismatch //ListGenericFoo<HashMap> foo3 = new ListGenericFoo<HashMap>(); LinkedList[] linkedLists = new LinkedList[10]; foo1.setFooArray(linkedLists); ArrayList[] arrayLists = new ArrayList[10]; foo2.setFooArray(arrayLists); } }
類聲明中:public class ListGenericFoo<T extends List>
這樣就規定了T必須是一個List繼承體系中的類,即實現了List接口的類。
此處注意,雖然List是一個接口,但是關鍵字仍然是extends而不是implements。
並且這個List也可以后加括號指明類型,如List<String>等。
當沒有指定泛型繼承的類型或接口時,默認使用T extends Object,所以默認情況下任何類型都可以作為參數傳入。
當不使用泛型時,比如那些聲明時帶有<T>的集合類型,如果使用時沒有指定類型,泛型類別為Object。不會報錯,但是會有警告。
<? extends SomeClass>是一個限界通配符(bounded wildcard),?代表了一個未知的類型,並且它是SomeClass的子類,也可以是SomeClass本身。
這里面SomeClass是統配符的上界(upper bound of the wildcard)。
相應的也有限定下界的,使用關鍵字super。
通配符所代表的其實是一組類型,但具體的類型是未知的。
類型通配聲明
看下面的代碼:
GenericFoo<Integer> foo1 = null; GenericFoo<Boolean> foo2 = null; //此時foo1只能接受GenericFoo<Integer>類型的實例,foo2只能接受GenericFoo<Boolean>類型的實例
如果希望有一個變量foo可以指向下面所有的實例:
//foo = new GenericFoo<ArrayList>(); //foo = new GenericFoo<LinkedList>();
可以這樣聲明:
GenericFoo<? extends List> foo = null; foo = new GenericFoo<ArrayList>(); foo = new GenericFoo<LinkedList>();
注意這種形式不同於前面的限制泛型可用類型時提到的形式。
前面提到的形式是在聲明泛型的類的時候限制了可以用的泛型類型,而現在這種形式是在使用的時候限制了引用的類型,使得引用指向繼承了某一個類或接口的類型。
如果該應用指向其他類型,則會編譯報錯:
//Error:Type mismatch foo = new GenericFoo<HashMap>();
也可以限制引用指向某個類或接口的繼承層次之上的類或接口:
比如:
//引用指向繼承層次之上 GenericFoo<? super List> ge= null; ge = new GenericFoo<Object>();
使用<?>或是<? extends SomeClass>的聲明方式,意味着您只能通過該名稱來取得所參考的實例的信息,或者是移除某些信息,但不能增加或者改寫它的信息。
因為只知道當中放置的是SomeClass的子類,但不確定是什么類的實例,編譯器不讓您加入信息,理由是,如果可以加入信息的話,那么您就得記得取回的是什么類型的實例,然后轉換為原來的類型方可進行操作,這樣就失去了使用泛型的意義。
另,GenericFoo<? extends Object>等價於GenericFoo<?>,但是它們與GenericFoo<Object>不同,因為GenericFoo<Object>限定了類型為Object。
參考資料
張龍老師Java SE視頻教程。
The Java Tutorials : Lesson: Generics (Updated)
http://docs.oracle.com/javase/tutorial/java/generics/index.html
Lesson: Generics
http://docs.oracle.com/javase/tutorial/extra/generics/index.html
Wildcards
http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
Java深度歷險值Java泛型
http://kb.cnblogs.com/page/93005/