您的位置:首页 > 编程语言 > Java开发

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


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