為什么說List<?>是type-safe而List不是type-safe的?
1、List<?>
compiler看到了你使用了wildcard ?,那么相當於你對compiler說:“我不知道這個List里面的element的runtime-type是什么,如果我嘗試對這個list或者list中取出來的object做一些type-specific的操作,你要給我一個compile-time-error來提醒我”。這樣就導致了2個結果:
1.1 list.get()返回類型為?,所以你只能用Object接收,Object足以確保type-safe,因為java中任何class都是Object的subclass。(當然,如果你非要使用類型強制轉換,轉換成什么阿貓阿狗的class,也沒人攔得住你,對此只能說“編譯器盡力了,你行你上啊”,反正ClassCastException什么的最有愛了)
2.2 list.put()除了null以外,任何參數都不接收。這也足以確保list中類型的type-safe,要知道,java的泛型的implementation是基於ERASURE(擦除)的,舉個具體的例子,LinkedList<E>的內部數據結構肯定是基於Node<E>,那么一個Node有2個field,E element和Node<E> next,而實際上在runtime環境中,LinkedList<String>中的Node並不是Node<String>,僅僅是Node,Node里面的element的類型也不是String,僅僅是Object,也就是說,compile-time的type-information都被抹除了(Quote: For backward-compatibility)。試想這么一個情景,Tom傳了一個List<Dog>給Mike,Mike的interface是List<?>,Mike往list中放了一個Cat(假設compiler沒有阻止Mike),然后Tom取出該List中所有的object並當成Dog使用(compiler會自動加上類型轉換的代碼——which is how java generics worked),然后Tom就悲劇地得到了一個ClassCastException——這就是為什么除了null其他參數都不接收的原因——阻止Mike隨便放東西進去。
2、List
raw-type就是這么個情況,相當於你對compiler說:“我並不在乎這個List里面的element的runtime-type是什么,不管我怎么操作這個list或者list中取出來的object,你都別管,實在看不過去就給我個warning就行了”。這種情況下:
2.1 list.get()返回類型為Object,當然,也是type-safe的(如果你不強制轉換的話)
2.2 list.put()的參數類型為Object,也就是說,你愛往里面放什么object就放什么object,還是上面那個例子,就算Tom給Mike的是List<String>,但由於Mike的interface是List,所以Mike放個BigInteger甚至什么Cat、Dog,compiler都不會阻止Mike(但是,要知道,Mike是無法得知其他人會怎么使用這個List的,比如說Mike無法得知Tom相信編譯器確保了list中的object都是String,但是由於Mike的raw-type interface,Tom就難免吃ClassCastException咯)
舉個具體的例子:
class Dog { public void bark() { } } class Cat { public void meow() { } } public class Foo { public static void foo1(List<?> list) { // Java generics are implemented upon ERASURE, so the runtime-type of elements in List<?> and List are both Object. if (list.isEmpty()) { return; } Object o = list.get(0); // Type safe if you don't do DOWN-CAST // list.add(new Dog()); // Won't compile } public static void foo2(List list) { if (list.isEmpty()) { return; } Object o = list.get(0); // Type safe if you don't do DOWN-CAST list.add(new Cat()); //! Compiler won't stop you, just gives you a little complaint } public static void main(String[] args) { List<Cat> cats = new LinkedList<Cat>(); cats.add(new Cat()); List<Dog> dogs = new LinkedList<Dog>(); dogs.add(new Dog()); foo1(cats); // relatively type-safe foo2(dogs); // dangerous for (Cat cat : cats) { // Cast from Object to Cat cat.meow(); } for (Dog dog : dogs) { //! Cast from Object to Dog, ClassCastException dog.bark(); } } }