effective java(25) 之列表优先于数组

来源:互联网 发布:上海迪士尼游客数据 编辑:程序博客网 时间:2024/05/18 11:27
effective java 之列表优先于数组


1、协变的(convariant):表示如果Sub为super的子类型,那么数组类型Sub[]就是super[]的子类型。
不可变的(invariant):对于任意两个不同的类型Type1和Type2,List<Type1>既不是List<Type2>的子类型,也不是List<Type2>的超类型。
不可具体化的(non-reifiable):指其运行时表示法包含的信息比它编译时表示法包含的信息更少的类型。


2、数组是协变的而泛型是不可变的


// Fails at runtime!  运行时异常
Object[] objectArray = new Long[1];  
objectArray[0] = "I don't fit in"; // Throws ArrayStoreException


// Won't compile  编译期就异常了
List<Object> ol = new ArrayList<Long>(); // Incompatible types  
ol.add("I don't fit in"); 


数组是在运行时才发现异常,而泛型则在编译时就发现异常,这就是数组是协变的造成的。
因为Long是Object的子类型,所以Long[]是Object[]的子类型,这样一来,在第二句将一个String插入到一个此时类型为Object的数组里在编译期是合法的(多态的特征是在运行时展现的),
然而,当运行的时候就会发现,把一个String的对象放到Long类型的容器中,这是不允许的。


对于泛型,由于在运行时,泛型类型会被擦除(正解决了C++中模板代码膨胀的问题),所以在编译其就会检查类型匹配问题。
由于泛型是不可变的,所以ArrayList<Long>并不是List<Object>的子类,在编译时编译器会给出错误信息。
这两个小小的例子还告诉我们,使用泛型可以将某些数组带来的运行时的错误提早到编译时被发现,这当然是所期望的。
还有一点,泛型是不可以用基本类型来做类型参数的,相应的,可以使用基本类型的自动装箱类型来实现。


4、数组是可具体化的,而泛型不是。
创建泛型、参数化类型或者是类型参数的数组都是非法的。如:new List<E>[]、new List<String>[]和new E[]都是非法的。这些在编译的时候会产生一个generic array creating错误。


//创建一个泛型数组,假设合法 
List<String>[] stringLists = new List<String>[1]; 
//创建并初始化一个包含单个元素的List<Integer>  
    List<Integer> intList = Arrays.asList(42);
    //将List<String>数组保存到一个Object数组变量中,这是合法的,因为数组和协变的。  
    Object[] objects = stringLists;
    //将List<Integer>保存到Object数组里唯一的元素中,这是可以的,因为泛型是通过擦除实现的。
    objects[0] = intList;  
    //我们从这个数组里唯一的列表中获取唯一的元素,编译器会自动地获取到元素转换成String,但它是一个Integer,因此,我们在运行时得到一个ClassCastException。
    String s= stringLists[0].get(0);  
    //为了防止这种情况(创建泛型数组),第一行就产生了一个编译时错误。  


5、唯一可具体化(reifiable)的参数化类型是无限制的通配符类型,如List<?>,Map<?,?>。虽然不常用,但是创建无限制通配类型的数组是合法的。


List<?>[] stringLists=new List<?>[1];//是合法的  


6、数组和泛型有着非常不同的类型规则。
当你得到泛型数组创建错误时,最好的解决办法通常是优先使用集合类型List<E>,而不是数组类型E<>。这样可能会损失一些性能或者简洁性,但是换回的却是更高的类型安全性和互用性。
数组是协变且可以具体化的;泛型是不可变的且可以被擦除的。
因此,数组提供了运行时的类型安全,但是没有编译时的类型安全,反之,对于泛型也一样。
一般来说,数组和泛型不能很好地混合使用。如果你发现自己将它们混合起来使用,并且得到了编译时错误或者警告,你的第一反应就应该是用列表代替数组。



每天努力一点,每天都在进步。

原创粉丝点击