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

参考资料: 《Java Generics and Collections》


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 的集合框架而不是原始的数组类型.
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值