2012年2月5日

有關 Java Generics 中的 (wildcards)


今天編程時遇到一些不明白的地方:我嘗試 extends 一個類時,而這個類裡的 constructor 或 method 含有一個Java Generics wildcards: <?> (或 <? extends Object>)的輸入參數 (如 List<? extends Map<String, ?>> data),當我嘗試對 data 作 put 的操作時,便得到 Compile error。

Error 如:

The method put(String, capture#3-of ? extends Object) in the type Map<String,capture#3-of ? extends Object> is not  applicable for the arguments (String, String)

在網上找到一些資料:

學到了:

當 declare  一個 Collection<Object> 時,這並不等於適用於所有類型 - "Collection<Object>, which, as we've just demonstrated, is not a supertype of all kinds of collections!",意思即創建時必須為只接受 Object ,如:
List<Object> list1 = new ArrayList<Object>(); // OKAY
list1.add("Hello"); // OKAY
List<Object> list2 = new ArrayList<String>(); // Compile Error
雖然 String 的 supertype 為 Object,但 compile 時會有 error。

那怎樣才可以接受所有類型呢? 這時須要使用 wildcards <?> (或 <? extends Object>)
List<?> list1;
list1= new ArrayList<String>(); // OKAY
list1= new ArrayList<Integer>(); // ALSO OKAY
當 Collection 被這樣 Declare 時,它可以任意被讀取(read)資料 (如調用 .get(idx)),不過卻不能寫入(write)資料 (如 .put(Obj) ):
List<?> list1 = new ArrayList<String>(); // OKAY
int size = list1.size(); // OKAY
list1.add("Hello"); // Compile Error
這樣有什麼好處呢? 其實這樣做是為了有更安全的編程考慮,在Compile 時已找出問題所在,特別當使用在 Method 的 Argument 上,這意味著這個 Argument 適用任意類型 (或是 extends 自某個 Supertype,即所謂 Bounded Wildcards),但又確保 Method 中可以讀取資料但不會修改 Collection 的資料。

當然,若果能肯定寫入時的類型規範,也可以使用 annotation 加 casting:
List<?> list1 = new ArrayList<String>();
...
@SuppressWarnings("unchecked")
List<String> list2 = (List<String>)list1;
list2.add("Hello");

沒有留言:

發佈留言