为什么说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();
        }
    }
}
复制代码

 

posted on 2017-09-12 20:15  阿郎  阅读(24389)  评论(0编辑  收藏  举报