Java泛型
2016-05-10 17:30
477 查看
PECS
请记住PECS原则:生产者(Producer)使用extends,消费者(Consumer)使用super。
生产者使用extends
如果你需要一个列表提供T类型的元素(即你想从列表中读取T类型的元素),你需要把这个列表声明成< extends T>,比如List< extends Integer>,因此你不能往该列表中添加任何元素。
消费者使用super
如果需要一个列表使用T类型的元素(即你想把T类型的元素加入到列表中),你需要把这个列表声明成< super T>,比如List< super Integer>,因此你不能保证从中读取到的元素的类型。
即是生产者,也是消费者
如果一个列表即要生产,又要消费,你不能使用泛型通配符声明列表,比如List<Integer>。
例子
请参考java.util.Collections里的copy方法(JDK1.7):
我们可以从Java开发团队的代码中获得到一些启发,copy方法中使用到了PECS原则,实现了对参数的保护。
Class RedApple extends Apple{}
Class Apple extends Fruit{}
Class Friut extends Food{}下面讨论List<? extends Class ?>和List<? super Class ?>用法。例如:
List<? extends Fruit> fruitEntity = new ArrayList<Apple>,左边表示List类型上限是Fruit,可以表示是Fruit,Apple,RedApple中的任一个;
此时你可能想到这样:fruitEntity.add(new Apple());
fruitEntity.add(new RedApple());看上去好像是没什么问题,但实际上这是错误的用法;以上不等同于List<Apple> fruitEntity = new ArrayList<Apple>,如果是这样便可以那样add;
List<? extends Fruit> fruitEntity = new ArrayList<Apple>虽然已经new个实例出来,但编译器是不能识别的,它是在编译是会根据你的New出来的类型自己会在其范围内找到对应该的类给你填上,但是我们写这样的代码它是不能识别的,它还是表示一个上限是Fruit的范围,所以我们在用add方法时会有这样的问题:比如fruitEntity.add(new
Apple());当前类不一定表示Apple类或者是其父类,它可能是RedApple类,这样你add(new Apple())明显是不合理的;总体是这样:在没编译时他不知道自己表示是哪个类,而却向其add一个具体的类是说不过去的。
与之对应的是List<? super Fruit> fruitEntity = new ArrayList<Fruit>;这种情况正好和上一个情况是相反的。它表示当前list类型下限为Fruit,即可能是Fruit,Food,Object同理它不等同于List<Fruit>
fruitEntity = new ArrayList<Fruit>;但是它是可以执行那样的add方法,即:fruitEntity.add(new Apple());fruitEntity.add(new RedApple());因为它表示的下限是Fruit ,向其中加RedApple,Apple是没有问题的,因为它们已被确定是其子类。同样的道理;fruitEntity.add(new Food()),fruitEntity.add(new Object());这样加是不行的,,它不知道上限是哪个,所以不能这样加!
在网上看了相关的,不过说的不是很清楚,在此按自己的理解理一下。
? 通配符类型
<? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类
<? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object
List<? extends Frut> 表示 “具有任何从Fruit继承类型的列表”,编译器无法确定List所持有的类型,所以无法安全的向其中添加对象。可以添加null,因为null 可以表示任何类型。所以List 的add 方法不能添加任何有意义的元素,但是可以接受现有的子类型List<Apple> 赋值。
由于,其中放置是从Fruit中继承的类型,所以可以安全地取出Fruit类型。
在使用Collection中的contains 方法时,接受Object 参数类型,可以不涉及任何通配符,编译器也允许这么调用。
List<? super Fruit> 表示“具有任何Fruit超类型的列表”,列表的类型至少是一个 Fruit 类型,因此可以安全的向其中添加Fruit 及其子类型。由于List<? super Fruit>中的类型可能是任何Fruit 的超类型,无法赋值为Fruit的子类型Apple的List<Apple>.
因为,List<? super Fruit>中的类型可能是任何Fruit 的超类型,所以编译器无法确定get返回的对象类型是Fruit,还是Fruit的父类Food 或 Object.
请记住PECS原则:生产者(Producer)使用extends,消费者(Consumer)使用super。
生产者使用extends
如果你需要一个列表提供T类型的元素(即你想从列表中读取T类型的元素),你需要把这个列表声明成< extends T>,比如List< extends Integer>,因此你不能往该列表中添加任何元素。
消费者使用super
如果需要一个列表使用T类型的元素(即你想把T类型的元素加入到列表中),你需要把这个列表声明成< super T>,比如List< super Integer>,因此你不能保证从中读取到的元素的类型。
即是生产者,也是消费者
如果一个列表即要生产,又要消费,你不能使用泛型通配符声明列表,比如List<Integer>。
例子
请参考java.util.Collections里的copy方法(JDK1.7):
我们可以从Java开发团队的代码中获得到一些启发,copy方法中使用到了PECS原则,实现了对参数的保护。
Class RedApple extends Apple{}
Class Apple extends Fruit{}
Class Friut extends Food{}下面讨论List<? extends Class ?>和List<? super Class ?>用法。例如:
List<? extends Fruit> fruitEntity = new ArrayList<Apple>,左边表示List类型上限是Fruit,可以表示是Fruit,Apple,RedApple中的任一个;
此时你可能想到这样:fruitEntity.add(new Apple());
fruitEntity.add(new RedApple());看上去好像是没什么问题,但实际上这是错误的用法;以上不等同于List<Apple> fruitEntity = new ArrayList<Apple>,如果是这样便可以那样add;
List<? extends Fruit> fruitEntity = new ArrayList<Apple>虽然已经new个实例出来,但编译器是不能识别的,它是在编译是会根据你的New出来的类型自己会在其范围内找到对应该的类给你填上,但是我们写这样的代码它是不能识别的,它还是表示一个上限是Fruit的范围,所以我们在用add方法时会有这样的问题:比如fruitEntity.add(new
Apple());当前类不一定表示Apple类或者是其父类,它可能是RedApple类,这样你add(new Apple())明显是不合理的;总体是这样:在没编译时他不知道自己表示是哪个类,而却向其add一个具体的类是说不过去的。
与之对应的是List<? super Fruit> fruitEntity = new ArrayList<Fruit>;这种情况正好和上一个情况是相反的。它表示当前list类型下限为Fruit,即可能是Fruit,Food,Object同理它不等同于List<Fruit>
fruitEntity = new ArrayList<Fruit>;但是它是可以执行那样的add方法,即:fruitEntity.add(new Apple());fruitEntity.add(new RedApple());因为它表示的下限是Fruit ,向其中加RedApple,Apple是没有问题的,因为它们已被确定是其子类。同样的道理;fruitEntity.add(new Food()),fruitEntity.add(new Object());这样加是不行的,,它不知道上限是哪个,所以不能这样加!
在网上看了相关的,不过说的不是很清楚,在此按自己的理解理一下。
关键字说明
? 通配符类型<? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类
<? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object
extends 示例
static class Food{} static class Fruit extends Food{} static class Apple extends Fruit{} static class RedApple extends Apple{} List<? extends Fruit> flist = new ArrayList<Apple>(); // complie error: // flist.add(new Apple()); // flist.add(new Fruit()); // flist.add(new Object()); flist.add(null); // only work for null
List<? extends Frut> 表示 “具有任何从Fruit继承类型的列表”,编译器无法确定List所持有的类型,所以无法安全的向其中添加对象。可以添加null,因为null 可以表示任何类型。所以List 的add 方法不能添加任何有意义的元素,但是可以接受现有的子类型List<Apple> 赋值。
Fruit fruit = flist.get(0); Apple apple = (Apple)flist.get(0);
由于,其中放置是从Fruit中继承的类型,所以可以安全地取出Fruit类型。
flist.contains(new Fruit()); flist.contains(new Apple());
在使用Collection中的contains 方法时,接受Object 参数类型,可以不涉及任何通配符,编译器也允许这么调用。
super 示例
List<? super Fruit> flist = new ArrayList<Fruit>(); flist.add(new Fruit()); flist.add(new Apple()); flist.add(new RedApple()); // compile error: List<? super Fruit> flist = new ArrayList<Apple>();
List<? super Fruit> 表示“具有任何Fruit超类型的列表”,列表的类型至少是一个 Fruit 类型,因此可以安全的向其中添加Fruit 及其子类型。由于List<? super Fruit>中的类型可能是任何Fruit 的超类型,无法赋值为Fruit的子类型Apple的List<Apple>.
// compile error: Fruit item = flist.get(0);
因为,List<? super Fruit>中的类型可能是任何Fruit 的超类型,所以编译器无法确定get返回的对象类型是Fruit,还是Fruit的父类Food 或 Object.
小结
extends 可用于的返回类型限定,不能用于参数类型限定。 super 可用于参数类型限定,不能用于返回类型限定。 >带有super超类型限定的通配符可以向泛型对易用写入,带有extends子类型限定的通配符可以向泛型对象读取。——《Core Java》
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树