[转]《Java Generics and Collections》读书笔记二:子类化与通配符

转自:http://www.diybl.com/course/3_program/java/javaxl/20071220/92739.html
1. 子类型与替换原则
List<Number> 是 Collection<Number> 的子类型, 但 List<Integer> 却不是 List<Number> 的子类型, 如下面代码:
List<Integer> ints = Arrays.asList(1,2);
List<Number> nums = ints;  // 编译错误
nums.add(3.14);  // 如果上面通过, 则List<Integer>现在就包含了Double类型的值了

反过来, List<Number> 也不是 List<Integer> 的子类型, 下面代码一样通不过:
List<Number> nums = Arrays.<Number>asList(2.78, 3.14);
List<Integer> ints = nums;  // 编译错误, 否则 List<Integer> 将包含 Double 类型的值
数组在这里比较特殊, 例如 Integer[] 就是 Number[] 的子类型, 可以进行赋值.


2. 通配符(Wildcards)
通配符使用 "?" 表示, 常用的有两种形式: ? extends T 和 ? super T
特殊情况: ? 是 ? extends Object 的简写形式
? extends E, 表示任何类型为 E 或者 E 的子类型都可以接受
下面方法声明来自 Collection 接口
public boolean addAll(Collection<? extends E> c);  // Collection 接口
List<Number> nums = new ArrayList<Number>();
List<Integer> ints = Arrays.asList(1, 2);
List<Double> dbls = Arrays.asList(2.78, 3.14);
nums.addAll(ints);
nums.addAll(dbls);
只要传入的集合类型是 E 或者 E 的子类, 该方法都可以接受.
我们也可以在声明变量的时候使用通配符, 但却不可以往集合中添加元素. 如下面代码:
List<Integer> ints = Arrays.asList(1,2);
List<? extends Number> nums = ints;
nums.add(3.14);  // 编译错误, 因为 nums 可以接受任何 Number 类型的元素, 但本身这却是个 List<Integer> 的对象

? super T, 表示任何类型为 T 或者 T 的超类型都可以接受
下面是来自 Collectins 类的 copy 方法
public static <T> void copy(List<? super T> dst, List<? extends T> src)  //Collections 类
List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
List<Integer> ints = Arrays.asList(5, 6);
Collections.copy(objs, ints);  // 隐式调用, 类型 T 为 Integer
Collections.<Object>copy(objs, ints);  // 类型 T 为 Object
Collections.<Number>copy(objs, ints);  // 类型 T 为 Number
Collections.<Integer>copy(objs, ints);  // 类型 T 为 Integer

copy 方法这样设计, 可以接受的参数范围就最大化了. 如果不使用通配符, 则只能复制相同类型的集合

3. Get-Put 原则
对于到底应该使用 ? extends E, ? super E, 还是根本不应该使用通配符, 有一个非常简单的基本原则.
Get-Put 原则: 当你仅仅需要从集合中取出元素时, 使用 extends; 如果需要往集合中添加元素, 则使用 super; 如果既要添加又要获取, 那就不要使用通配符.
public static <T> void copy(List<? super T> dest, List<? extends T> src)
我们只需要从 src 中取出元素, 所以使用 ? extends T 形式; 而 dest 则只进行添加操作, 所以就使用 ? super T 形式. 这样方法可以接受的参数范围就最大化了.
每一条原则都有例外, 我们的 Get-Put 原则也有它的特殊情况. 使用 ? extends E 声明的集合虽然不能够添加元素, 但却可以添加 null 值, 因为 null 是任何类型的子类; 同样使用 ? super E 声明的集合不能取出元素, 但却可以取出 Object 类型, 因为 Object 是所有类型的超类.
Java 并不允许 Collection<? extends Number super Integer> 这样的写法, 不过以后可能会支持, 因为这在某些情况下可能会很有用.
最后, List<String> 和 List<? extends String> 的类型是不一样的, 即使 String 是 final 型的而不能有子类. 实际上, List<String> 是 List<? extends String> 的子类型. 你可以通过子类型替换原则, 使用赋值语句来验证这一点.


4. 数组 Arrays
Java 的数组比较特别, 对于 T[] 和 S[], 只要 T 是 S 的子类型, 则 T[] 就是 S[] 的子类型. 如下面代码:
Integer[] ints = new Integer[10];
Number[] nums = ints;
nums[0] = 1;
nums[1] = 5.5;  // 运行时抛出异常 ArrayStoreException, Java 编译器并不能阻止你把一个 Double 添加到一个 Integer 数组中去.
使用 Java 的集合框架要优于使用数组, 因为集合更加灵活, 也更加强大, 常用的功能都已经实现. 使用数组的唯一理由是: 大量的原始数据类型, 可能可以获得性能上的提升. 不过一定要谨记程序优化第一法则: 不要优化你的程序, 除非经过严格而精确的测量证明存在性能问题. 另外, 有些情况下, 由于某些遗留系统的兼容问题, 你可能仍需要使用数组.

总之: 尽量使用 Java 的集合框架而不是原始的数组类型.

原文地址:https://www.cnblogs.com/apigiraffe/p/2685105.html