ジェネリックについて調べているときに PECS ( Producer extends
and Consumersuper
の略) に出会いました。
extends
との間の混乱を解決するために PECS を使用する方法を誰か説明してもらえますかsuper
?
ジェネリックについて調べているときに PECS ( Producer extends
and Consumersuper
の略) に出会いました。
extends
との間の混乱を解決するために PECS を使用する方法を誰か説明してもらえますかsuper
?
tl;dr:「PECS」はコレクションの観点からのものです。ジェネリック コレクションからのみアイテムをプルする場合、それはプロデューサーであり、使用する必要がありますextends
。アイテムを詰め込むだけの場合、それは消費者であり、使用する必要がありますsuper
。同じコレクションで両方を行う場合は、 または のいずれextends
かを使用しないでくださいsuper
。
パラメーターとして物のコレクションを受け取るメソッドがあり、Collection<Thing>
.
ケース 1: コレクションを調べて、各アイテムを操作したい。
リストはプロデューサーなので、Collection<? extends Thing>
.
その理由は、 aCollection<? extends Thing>
は の任意のサブタイプを保持できるため、操作を実行するThing
と各要素は a として動作するからです。(実行時にコレクションの特定のサブタイプが保持されているかを知ることができないため、Thing
実際には (null を除く) を に追加することはできません。)Collection<? extends Thing>
Thing
ケース 2: コレクションに追加したい場合。
次に、リストはコンシューマーであるため、Collection<? super Thing>
.
ここでの理由は、 とは異なりCollection<? extends Thing>
、実際のパラメーター化された型が何であるかに関係なく、Collection<? super Thing>
常に a を保持できるということです。ここでは、 aを追加Thing
できる限り、リストに既に何があるかは気にしません。Thing
これが? super Thing
保証するものです。
コンピュータ サイエンスにおけるこの背後にある原則は、
? extends MyClass
,? super MyClass
およびMyClass
以下の図は、概念を説明する必要があります。写真提供:アンドレイ・チュキン
PECS (生産者extends
と消費者super
)
ニーモニック → Get (拡張)と P u t (スーパー) の原則。
この原則は次のように述べています。
extends
構造体からのみ値を取得する場合は、ワイルドカードを使用します。super
構造体に値を入れるだけの場合は、ワイルドカードを使用してください。Java での例:
class Super {
Number testCoVariance() {
return null;
}
void testContraVariance(Number parameter) {
}
}
class Sub extends Super {
@Override
Integer testCoVariance() {
return null;
} //compiles successfully i.e. return type is don't care(Integer is subtype of Number)
@Override
void testContraVariance(Integer parameter) {
} //doesn't support even though Integer is subtype of Number
}
Liskov Substitution Principle (LSP) は、「<strong>プログラム内のオブジェクトは、そのプログラムの正確性を変更することなく、そのサブタイプのインスタンスに置き換え可能であるべきである」と述べています。
プログラミング言語の型システム内での型付け規則
この一般的な現象を説明するために、配列の型を考えてみましょう。タイプ Animal の場合、タイプ Animal[] を作成できます。
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
-無制限のワイルドカード。それはすべてのタイプの家族を表します。get と put の両方の場合に使用します。? extends T
(子孫の治世) -上限T
のあるワイルドカード。継承階層の最上位のクラスです。構造体から値を取得するだけの場合は、ワイルドカードを使用します。T
extends
? super T
(祖先の治世) -下限T
を持つワイルドカード。継承階層の最下位クラスです。値を構造体に入れるだけの場合は、ワイルドカードを使用します。T
super
注: ワイルドカード?
はゼロまたは 1 回を意味し、不明なタイプを表します。ワイルドカードは、パラメーターの型として使用できますが、ジェネリック メソッドの呼び出し、ジェネリック クラス インスタンスの作成の型引数として使用されることはありません (つまり、ワイルドカードを使用した場合、プログラムの他の場所で使用されていないものを参照しますT
) 。
import java.util.ArrayList;
import java.util.List;
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 {
public static void main(String[] args) {
//? extends Shape i.e. can use any sub type of Shape, here Shape is Upper Bound in inheritance hierarchy
List<? extends Shape> intList5 = new ArrayList<Shape>();
List<? extends Shape> intList6 = new ArrayList<Cricle>();
List<? extends Shape> intList7 = new ArrayList<Rectangle>();
List<? extends Shape> intList9 = new ArrayList<Object>();//ERROR.
//? super Shape i.e. can use any super type of Shape, here Shape is Lower Bound in inheritance hierarchy
List<? super Shape> inList5 = new ArrayList<Shape>();
List<? super Shape> inList6 = new ArrayList<Object>();
List<? super Shape> inList7 = new ArrayList<Circle>(); //ERROR.
//-----------------------------------------------------------
Circle circle = new Circle();
Shape shape = circle; // OK. Circle IS-A Shape
List<Circle> circles = new ArrayList<>();
List<Shape> shapes = circles; // ERROR. List<Circle> is not subtype of List<Shape> even when Circle IS-A Shape
List<? extends Circle> circles2 = new ArrayList<>();
List<? extends Shape> shapes2 = circles2; // OK. List<? extends Circle> is subtype of List<? extends Shape>
//-----------------------------------------------------------
Shape shape2 = new Shape();
Circle circle2= (Circle) shape2; // OK. with type casting
List<Shape> shapes3 = new ArrayList<>();
List<Circle> circles3 = shapes3; //ERROR. List<Circle> is not subtype of List<Shape> even Circle is subetype of Shape
List<? super Shape> shapes4 = new ArrayList<>();
List<? super Circle> circles4 = shapes4; //OK.
}
/*
* Example for an upper bound wildcard (Get values i.e Producer `extends`)
*
* */
public void testCoVariance(List<? extends Shape> list) {
list.add(new Object());//ERROR
list.add(new Shape()); //ERROR
list.add(new Circle()); // ERROR
list.add(new Square()); // ERROR
list.add(new Rectangle()); // ERROR
Shape shape= list.get(0);//OK 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 Object());//ERROR
list.add(new Shape());//OK
list.add(new Circle());//OK
list.add(new Square());//OK
list.add(new Rectangle());//OK
Shape shape= list.get(0); // ERROR. Type mismatch, so list acts only as consumer
Object object= list.get(0); //OK 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.
*/
}
}
共分散と反分散は、型に基づいて互換性を決定します。いずれの場合も、分散は有向関係です。共分散は、「同じ方向で異なる」または異なると翻訳できますが、反分散は「反対方向で異なる」または反対の違いを意味します。共変型と反変型は同じではありませんが、それらの間には相関関係があります。名前は、相関の方向を暗示しています。
https://stackoverflow.com/a/54576828/1697099
https://stackoverflow.com/a/64888058/1697099
簡単に言えば、PECS を覚えておくための 3 つの簡単なルールです。
<? extends T>
タイプのオブジェクトを取得する必要がある場合は、ワイルドカードを使用します。T
<? super T>
に入れる必要がある場合は、ワイルドカードを使用します。T
別の質問への回答で説明したように、PECS は Josh Bloch によって作成された記憶装置であり、生産者extends
、消費者を思い出すのに役立ちます。super
これは、メソッドに渡されるパラメーター化された型が のインスタンスを生成する
T
場合(それらは何らかの方法で取得される)、? extends T
を使用する必要があることを意味します。これは、 のサブクラスのインスタンスT
もT
.メソッドに渡されるパラメーター化された型が のインスタンスを消費する
T
(何かを行うために渡される) 場合、? super T
を使用する必要がありT
ますT
。たとえば、 はでComparator<Number>
使用できます。はを操作できないため、機能しません。Collection<Integer>
? extends T
Comparator<Integer>
Collection<Number>
一般に、一部のメソッドのパラメーターには? extends T
andのみを使用する必要があることに注意してください。? super T
メソッドはT
、ジェネリック戻り型の型パラメーターとして使用する必要があります。
共分散: サブタイプを受け入れる
反分散 :スーパータイプを受け入れる
共変型は読み取り専用ですが、反変型は書き込み専用です。