PECSは、「プロデューサーは、消費者のスーパーを拡張」を参照します。パラメータ化された型は、プロデューサを表す場合言い換えれば、使用は、<?T延び>、それは消費者、使用表す場合<スーパーTを?>。
私は理解していませんか?
見てください<を?Tは拡張>と<?スーパーT>異なります。
<? extends T>
そして、<? super T>
Javaのジェネリック「ワイルドカード(ワイルドカード)」と「境界線(境界)」という概念です。
<? extends T>
:手段「上限ワイルドカード(上界ワイルドカード)」<? super T>
:それは、「下限ワイルドカード(下の境界ワイルドカード)」を指し、
タイプ定義の数を想像してみて
// レフ1 クラス食品{} // レフ2 クラスフルーツ延び食品{} クラス肉が延び食品{} // レフ3 クラス Appleが延びフルーツ{} クラスバナナが延びフルーツ{} クラス豚肉が延び肉{} クラス牛肉が延び肉{} // レフ4 クラス RedAppleは延びアップル{} クラス GreenAppleが延び }アップル{
コンテナ、プレートクラスを定義します。プレートは、一般的な「もの」を置くことができます。
クラスプレート<T> { プライベートTアイテム。 公共プレート(T T){アイテム= T;} 公共 ボイド集合(T T){アイテム= T;} パブリック TのGET(){ 戻り項目;} }
今、私はもちろん、論理的にフルーツプレートがリンゴをインストールすることができ、「フルーツプレート」を定義します。
プレート<フルーツ> P = 新しいプレート<アップル>(新しいアップル());
しかし、実際にはJavaコンパイラは、この操作を許可していません。彼は、文句を言うでしょうに変換することができない「Appleのプレートがロードされた」「保持フルーツプレート。」
実際には、良い説明:
- アップルは、IS-Aフルーツ
- アップル皿は-IS NOT-ロードされたフルーツプレートを設置しました
だから、コンテナ間の継承関係は、原料で満たされますが、コンテナの間には継承関係が存在しない場合でも。だから、と<? extends T>
し、<? super T>。
ワイルドカード上限 Plate<? extends Fruit>
青色に示す下部領域カバレッジを。
下部は、ワイルドカード結合 Plate<? super Fruit>
下図の赤の領域を覆います。
その後PECS、別の例を見て:
ここでのスタックインタフェースシンプルなAPIは以下のとおりです。
public class Stack<E>{ public Stack(); public void push(E e): public E pop(); public boolean isEmpty(); //按顺序将一系列元素全部放入Stack中,你可能想到的实现方式如下: public void pushAll(Iterable<E> src){ for(E e : src) push(e) } }
这时,有个Stack<Number>,想要灵活的处理Integer,Long等Number的子类型的集合:
Stack<Number> numberStack = new Stack<Number>(); Iterable<Integer> integers = ....; numberStack.pushAll(integers);
此时代码编译无法通过,因为对于类型Number和Integer来说,虽然后者是Number的子类,但是对于任意Number集合(如List<Number>)不是Integer集合(如List<Integer>)的超类,因为泛型是不可变的。
幸好java提供了一种叫有限通配符的参数化类型,pushAll参数替换为“E的某个子类型的Iterable接口”:
public void pushAll(Iterable<? extends E> src){ for (E e: src) push(e); }
这样就可以正确编译了,这里的<? extends E>就是所谓的 producer-extends。这里的Iterable就是生产者,要使用<? extends E>。因为Iterable<? extends E>可以容纳任何E的子类。在执行操作时,可迭代对象的每个元素都可以当作是E来操作。
与之对应的是:假设有一个方法popAll()方法,从Stack集合中弹出每个元素,添加到指定集合中去:
public void popAll(Collection<E> dst){ if(!isEmpty()){ dst.add(pop()); } }
假设有一个Stack<Number>和Collection<Object>对象:
Stack<Number> numberStack = new Stack<Number>(); Collection<Object> objects = ...; numberStack.popAll(objects);
同样上面这段代码也无法通过,解决的办法就是使用Collection<? super E>。这里的objects是消费者,因为是添加元素到objects集合中去。使用Collection<? super E>后,无论objects是什么类型的集合,满足一点的是他是E的超类,所以不管这个参数化类型具体是什么类型都能将E装进objects集合中去。
btw,我们反过来呢?以下两种均不能通过编译。
public void pushAll(Iterable<? super E> src){ for (E e: src) push(e); }
因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是E的基类,那往里存粒度比E小的都可以。但往外读取元素就费劲了,只有所有类的基类Object对象才能装下。但这样的话,元素的类型信息就全部丢失。Stack自然也无法存E的基类。
public void popAll(Collection<? extends E> dst){ if(!isEmpty()){ dst.add(pop()); } }
原因是编译器只知道容器内是Fruit或者它的派生类,但具体是什么类型不知道。编译器在?标上一个占位符:CAP#1,来表示捕获一个E或E的子类,具体是什么类不知道,代号CAP#1。然后无论是想往里插入A或者B或者C编译器都不知道能不能和这个CAP#1匹配,所以就都不允许。
This means that when a parameterized type being passed to a method will produce instances of T
(they will be retrieved from it in some way), ? extends T
should be used, since any instance of a subclass of T
is also a T
.
When a parameterized type being passed to a method will consume instances of T
(they will be passed to it to do something), ? super T
should be used because an instance of T
can legally be passed to any method that accepts some supertype of T
. A Comparator<Number>
could be used on a Collection<Integer>
, for example. ? extends T
would not work, because a Comparator<Integer>
could not operate on a Collection<Number>
.
--from stackOverFlow https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super
另附阿里java开发手册第一章第五节第6条
6.【强制】泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用add方 法,而<? super T>不能使用get方法,作为接口调用赋值时易出错。 说明:扩展说一下PECS(Producer Extends Consumer Super)原则:第一、频繁往外读取内 容的,适合用<? extends T>。第二、经常往里插入的,适合用<? super T>。
https://blog.csdn.net/rj08zhou/article/details/45063451