今天編程時遇到一些不明白的地方:我嘗試 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)
在網上找到一些資料:
- http://stackoverflow.com/questions/4543592/help-with-java-generics-cannot-use-object-as-argument-for-extends-object
- http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
- http://docs.oracle.com/javase/tutorial/extra/generics/index.html
學到了:
當 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");
沒有留言:
發佈留言