Java泛型:List 與List的區別


為什么說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();
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。