840

ジェネリックについて調べているときに PECS ( Producer extendsand Consumersuperの略) に出会いました。

extendsとの間の混乱を解決するために PECS を使用する方法を誰か説明してもらえますかsuper?

4

15 に答える 15

961

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保証するものです。

于 2010-04-27T17:37:26.150 に答える
626

コンピュータ サイエンスにおけるこの背後にある原則は、

  • 共分散: ? extends MyClass,
  • 反変性:? super MyClassおよび
  • 不変性/不変性:MyClass

以下の図は、概念を説明する必要があります。写真提供:アンドレイ・チュキン

共分散と反分散

于 2013-11-02T06:34:38.217 に答える
77

PECS (生産者extendsと消費者super)

ニーモニック → Get (拡張)と P u t (スーパー) の原則。

  • この原則は次のように述べています。

    • extends構造体からのみ値を取得する場合は、ワイルドカードを使用します。
    • super構造体に値を入れるだけの場合は、ワイルドカードを使用してください。
    • また、get と put の両方でワイルドカードを使用しないでください。

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[] を作成できます。

  • covariant : Cat[] は Animal[] です。
  • contravariant : Animal[] は Cat[] です。
  • invariant : Animal[] は Cat[] ではなく、Cat[] は 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のあるワイルドカード。継承階層の最上位のクラスです構造体から値を取得するだけの場合は、ワイルドカードを使用します。Textends
  • 反分散: ? super T(祖先の治世) -下限Tを持つワイルドカード。継承階層の最下位クラスです。値を構造体に入れるだけの場合は、ワイルドカードを使用します。Tsuper

注: ワイルドカード?ゼロまたは 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

  • 共分散: サブタイプを受け入れます (読み取り専用、すなわちプロデューサー)
  • 反変性: スーパータイプを受け入れる (書き込みのみ、すなわち Consumer)
于 2015-07-26T06:33:49.120 に答える
30

簡単に言えば、PECS を覚えておくための 3 つの簡単なルールです。

  1. コレクションから<? extends T>タイプのオブジェクトを取得する必要がある場合は、ワイルドカードを使用します。T
  2. タイプのオブジェクトをコレクション<? super T>に入れる必要がある場合は、ワイルドカードを使用します。T
  3. 両方を満たす必要がある場合は、ワイルドカードを使用しないでください。それと同じくらい簡単です。
于 2014-11-13T06:51:24.617 に答える
25

別の質問への回答で説明したように、PECS は Josh Bloch によって作成された記憶装置であり生産extends、消費者を思い出すのに役立ちます。super

これは、メソッドに渡されるパラメーター化された型が のインスタンスを生成するT場合(それらは何らかの方法で取得される)、? extends Tを使用する必要があることを意味します。これは、 のサブクラスのインスタンスTT.

メソッドに渡されるパラメーター化された型が のインスタンスを消費するT(何かを行うために渡される) 場合、? super Tを使用する必要がありTますT。たとえば、 はでComparator<Number>使用できます。はを操作できないため、機能しません。Collection<Integer>? extends TComparator<Integer>Collection<Number>

一般に、一部のメソッドのパラメーターには? extends Tandのみを使用する必要があることに注意してください。? super TメソッドはT、ジェネリック戻り型の型パラメーターとして使用する必要があります。

于 2010-04-27T17:32:52.383 に答える
4

共分散: サブタイプを受け入れる
分散 :スーパータイプを受け入れる

共変型は読み取り専用ですが、反変型は書き込み専用です。

于 2019-02-07T15:33:37.840 に答える