PECSとは何ですか(プロデューサーは消費者スーパーを拡張します)?
-
01-10-2019 - |
質問
私はPECSに出会いました(略です プロデューサー extends
そして消費者 super
)ジェネリックを読んでいる間。
誰かが私にPECを使用して混乱を解決する方法を説明できますか extends
と super
?
解決
tl; dr: 「PECS」はコレクションの観点からです。あなたがいる場合 それだけ 一般的なコレクションからアイテムを引くと、それはプロデューサーであり、あなたは使用する必要があります extends
;あなたがいる場合 それだけ アイテムの詰め物、それは消費者であり、あなたは使用する必要があります super
. 。同じコレクションで両方を使用している場合、どちらも使用しないでください extends
また super
.
パラメーターとして物事のコレクションを取る方法があると仮定 Collection<Thing>
.
ケース1:コレクションを調べて、各アイテムで物事を行います。
次に、リストはaです プロデューサー, 、したがって、aを使用する必要があります Collection<? extends Thing>
.
理由は、a Collection<? extends Thing>
のサブタイプを保持できます Thing
, 、したがって、各要素はaとして動作します Thing
操作を実行するとき。 (実際には何も追加できません Collection<? extends Thing>
, 、実行時にどちらを知ることができないからです 明確 のサブタイプ Thing
コレクションが保持します。)
ケース2:コレクションに物を追加したい。
次に、リストはaです 消費者, 、したがって、aを使用する必要があります Collection<? super Thing>
.
ここでの理由は、それとは違っています Collection<? extends Thing>
, Collection<? super Thing>
いつでも保持できます Thing
実際のパラメーター化されたタイプが何であれ。ここでは、それが許可される限り、すでにリストにあるものを気にしません Thing
追加する;これは何 ? super Thing
保証。
他のヒント
コンピューターサイエンスのこの背後にある原則は呼ばれます
- 共分散:
? extends MyClass
, - 矛盾:
? super MyClass
と - 不変/不変性:
MyClass
以下の写真は、概念を説明する必要があります。写真礼儀: アンドレイ・ティウキン
PECS(プロデューサー extends
そして消費者 super
)
ニーモニック→原則を取得して配置します。
この原則は次のとおりです。
- 構造物から値を取得する場合は、ワイルドカードを拡張します。
- 構造に値を配置するときは、スーパーワイルドカードを使用します。
- また、ワイルドカードを使用しても使用してはいけません。
Javaの例:
class Super {
Object testCoVariance(){ return null;} //Covariance of return types in the subtype.
void testContraVariance(Object parameter){} // Contravariance of method arguments in the subtype.
}
class Sub extends Super {
@Override
String testCoVariance(){ return null;} //compiles successfully i.e. return type is don't care(String is subtype of Object)
@Override
void testContraVariance(String parameter){} //doesn't support even though String is subtype of Object
}
リスコフ代替原則: Sがtのサブタイプの場合、タイプtのオブジェクトはタイプSのオブジェクトに置き換えることができます。
プログラミング言語のタイプシステム内で、タイピングルール
- 共変動 タイプ(≤)の順序付けを保持している場合、より具体的なものからより一般的なものまでの型を注文します。
- 矛盾 この順序が逆転する場合。
- 不変 または、これらのいずれも適用されない場合は、非変動。
- 読み取り専用のデータ型(ソース)ができます 共変動;
- 書き込みのみのデータ型(シンク)はそうです 矛盾.
- ソースとシンクの両方として機能する可変データ型は 不変.
この一般的な現象を説明するには、アレイタイプを検討してください。タイプの動物のために、私たちはタイプの動物を作ることができます[
- 共変動: :猫[]は動物です。
- 矛盾: :動物[]は猫です。
- 不変: :動物[]は猫ではなく、猫[]は動物ではありません。
Javaの例:
Object name= new String("prem"); //works
List<Number> numbers = new ArrayList<Integer>();//gets compile time error
Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
myNumber[0] = 3.14; //attempt of heap pollution i.e. at runtime gets java.lang.ArrayStoreException: java.lang.Double(we can fool compiler but not run-time)
List<String> list=new ArrayList<>();
list.add("prem");
List<Object> listObject=list; //Type mismatch: cannot convert from List<String> to List<Object> at Compiletime
跳ねる(つまり、どこかに向かっている) ワイルドカード :ワイルドカードには3つの異なるフレーバーがあります。
- 侵害/非分散:
?
また? extends Object
- バウンドされていない ワイルドカード。それはあらゆるタイプの家族を表しています。両方を取得して置くときに使用します。 - 共分散:
? extends T
(のサブタイプであるあらゆるタイプのファミリT
) - anのワイルドカード 上界.T
それは アッパー- 継承階層のほとんどのクラス。 ANを使用してくださいextends
あなただけのときのワイルドカード 得る 構造からの値。 - コントラバリケンス:
? super T
(のスーパータイプであるあらゆるタイプのファミリT
) - aのワイルドカード 下限.T
それは 低い- 継承階層のほとんどのクラス。使うsuper
あなただけのときのワイルドカード 置く 構造への値。
注:ワイルドカード ?
意味 ゼロまたは1回, 、未知のタイプを表します。ワイルドカードは、パラメーターのタイプとして使用でき、一般的なメソッドの呼び出しのタイプ引数として使用されません。 T
)
class Shape { void draw() {}}
class Circle extends Shape {void draw() {}}
class Square extends Shape {void draw() {}}
class Rectangle extends Shape {void draw() {}}
public class Test {
/*
* Example for an upper bound wildcard (Get values i.e Producer `extends`)
*
* */
public void testCoVariance(List<? extends Shape> list) {
list.add(new Shape()); // Error: is not applicable for the arguments (Shape) i.e. inheritance is not supporting
list.add(new Circle()); // Error: is not applicable for the arguments (Circle) i.e. inheritance is not supporting
list.add(new Square()); // Error: is not applicable for the arguments (Square) i.e. inheritance is not supporting
list.add(new Rectangle()); // Error: is not applicable for the arguments (Rectangle) i.e. inheritance is not supporting
Shape shape= list.get(0);//compiles so list act as produces only
/*You can't add a Shape,Circle,Square,Rectangle to a List<? extends Shape>
* You can get an object and know that it will be an Shape
*/
}
/*
* Example for a lower bound wildcard (Put values i.e Consumer`super`)
* */
public void testContraVariance(List<? super Shape> list) {
list.add(new Shape());//compiles i.e. inheritance is supporting
list.add(new Circle());//compiles i.e. inheritance is supporting
list.add(new Square());//compiles i.e. inheritance is supporting
list.add(new Rectangle());//compiles i.e. inheritance is supporting
Shape shape= list.get(0); // Error: Type mismatch, so list acts only as consumer
Object object= list.get(0); // gets an object, but we don't know what kind of Object it is.
/*You can add a Shape,Circle,Square,Rectangle to a List<? super Shape>
* You can't get an Shape(but can get Object) and don't know what kind of Shape it is.
*/
}
}
public class Test {
public class A {}
public class B extends A {}
public class C extends B {}
public void testCoVariance(List<? extends B> myBlist) {
B b = new B();
C c = new C();
myBlist.add(b); // does not compile
myBlist.add(c); // does not compile
A a = myBlist.get(0);
}
public void testContraVariance(List<? super B> myBlist) {
B b = new B();
C c = new C();
myBlist.add(b);
myBlist.add(c);
A a = myBlist.get(0); // does not compile
}
}
私が説明するように 私の答え 別の質問にとって、PECSはJosh Blochによって作成されたニーモニックデバイスです。 pロドーサー extends
, cオンスマー super
.
これは、パラメーター化されたタイプがメソッドに渡されるときになることを意味します 生産 のインスタンス
T
(彼らは何らかの形でそれから回収されます)、? extends T
のサブクラスのインスタンスはT
AもですT
.メソッドに渡されるパラメーター化されたタイプが 消費 のインスタンス
T
(彼らは何かをするためにそれに渡されます)、? super T
のインスタンスのために使用する必要がありますT
いくつかのスーパータイプを受け入れる方法に法的に渡すことができますT
. 。 aComparator<Number>
で使用できますCollection<Integer>
, 、 例えば。? extends T
aComparator<Integer>
で動作できませんでしたCollection<Number>
.
通常、使用する必要があることに注意してください ? extends T
と ? super T
いくつかの方法のパラメーターの場合。メソッドはただ使用する必要があります T
一般的な返品タイプのタイプパラメーターとして。
一言で言えば、PECを覚えておくべき3つの簡単なルール:
- 使用
<? extends T>
タイプのオブジェクトを取得する必要がある場合はワイルドカードT
コレクションから。 - 使用
<? super T>
ワイルドカードタイプのオブジェクトを配置する必要がある場合T
コレクションで。 - 両方のものを満たす必要がある場合は、ワイルドカードを使用しないでください。それと同じくらい簡単です。
(ジェネリックのワイルドカードでは十分な例ではないので、答えを追加します)
// Source
List<Integer> intList = Arrays.asList(1,2,3);
List<Double> doubleList = Arrays.asList(2.78,3.14);
List<Number> numList = Arrays.asList(1,2,2.78,3.14,5);
// Destination
List<Integer> intList2 = new ArrayList<>();
List<Double> doublesList2 = new ArrayList<>();
List<Number> numList2 = new ArrayList<>();
// Works
copyElements1(intList,intList2); // from int to int
copyElements1(doubleList,doublesList2); // from double to double
static <T> void copyElements1(Collection<T> src, Collection<T> dest) {
for(T n : src){
dest.add(n);
}
}
// Let's try to copy intList to its supertype
copyElements1(intList,numList2); // error, method signature just says "T"
// and here the compiler is given
// two types: Integer and Number,
// so which one shall it be?
// PECS to the rescue!
copyElements2(intList,numList2); // possible
// copy Integer (? extends T) to its supertype (Number is super of Integer)
private static <T> void copyElements2(Collection<? extends T> src,
Collection<? super T> dest) {
for(T n : src){
dest.add(n);
}
}
この階層を仮定しましょう:
class Creature{}// X
class Animal extends Creature{}// Y
class Fish extends Animal{}// Z
class Shark extends Fish{}// A
class HammerSkark extends Shark{}// B
class DeadHammerShark extends HammerSkark{}// C
PEを明確にしましょう - プロデューサーは拡張します:
List<? extends Shark> sharks = new ArrayList<>();
このリストに「サメ」を拡張するオブジェクトを追加できないのはなぜですか?お気に入り:
sharks.add(new HammerShark());//will result in compilation error
タイプA、B、またはCのリストがあるので 実行時に, 、タイプA、B、またはCのオブジェクトを追加することはできません。これは、Javaで許可されていない組み合わせになる可能性があるためです。
実際には、コンパイラは実際にCompiletimeでBを追加することを確認できます。
sharks.add(new HammerShark());
...しかし、実行時にBがリストタイプのサブタイプまたはスーパータイプであるかどうかを判断する方法はありません。実行時には、リストタイプはA、B、Cのタイプのいずれかにすることができます。そのため、たとえばDeadhammersharkのリストにHammerskark(スーパータイプ)を追加することはできません。
*あなたは言うでしょう:「わかりました、でもそれが最小のタイプなので、なぜハマースカークを追加できないのですか?」回答:それは最小です 君 知る。しかし、Hammerskarkも他の誰かによって拡張することができ、あなたは同じシナリオになります。
CSを明確にしましょう - 消費者スーパー:
同じ階層でこれを試すことができます:
List<? super Shark> sharks = new ArrayList<>();
何となぜあなたが できる このリストに追加しますか?
sharks.add(new Shark());
sharks.add(new DeadHammerShark());
sharks.add(new HammerSkark());
上記の種類のオブジェクトを追加できます。なぜなら、サメ(A、B、C)の下は、常にサメ(X、Y、Z)の上のすべてのサブタイプになるためです。理解しやすい。
君 できません サメの上にタイプを追加します 実行時に 追加されたオブジェクトのタイプは、リストの宣言されたタイプ(x、y、z)よりも階層で高くなる可能性があります。これは許可されていません。
しかし、なぜこのリストから読めないのですか? (私はあなたがそれから要素を得ることができるということですが、あなたはそれをオブジェクト以外に割り当てることはできませんo)::
Object o;
o = sharks.get(2);// only assignment that works
Animal s;
s = sharks.get(2);//doen't work
実行時には、リストのタイプはa:x、y、z、...より上の任意のタイプにすることができます...コンパイラは割り当てステートメントをコンパイルできますが(正しいと思われます)、 実行時に s(動物)のタイプは、宣言されたタイプのリスト(クリーチャーである可能性があるか、それ以上)よりも階層で低くなる可能性があります。これは許可されていません。
総括する
を使用しております <? super T>
リスト内のt以下のタイプのオブジェクトを追加するには。 それから読むことはできません。
を使用しております <? extends T>
リストからtの等またはt以下のタイプのオブジェクトを読み取るため。 要素を追加することはできません。
これを覚えて:
消費者は食べる 夕食(素晴らしい);プロデューサー 拡張 彼の親の工場
共分散: :サブタイプを受け入れます
違反: :スーパータイプを受け入れます
共変動タイプは読み取り専用ですが、矛盾したタイプは書き込みのみです。
実生活の例を使用して(いくつかの単純化を伴う):
- リストに類似して貨物車を備えた貨物列車を想像してください。
- あなたはできる 置く 貨物が持っている場合、貨物車の貨物 同じまたは小さいサイズ 貨物車よりも=
<? super FreightCarSize>
- あなたはできる アンロード 持っている場合は、貨物車からの貨物 十分な場所 (貨物のサイズ以上)あなたのデポの中の=
<? extends DepotSize>