your programing

PECS (Producer Extends Consumer Super) 란 무엇입니까?

lovepro 2020. 9. 30. 11:12
반응형

PECS (Producer Extends Consumer Super) 란 무엇입니까?


제네릭을 읽는 동안 PECS ( Producer extendsand Consumer의super 약자 )를 발견했습니다.

누군가 PECS를 사용하여 extends사이의 혼란을 해결하는 방법을 설명해 줄 수 있습니까 super?


tl; dr : "PECS"는 컬렉션의 관점에서 볼 수 있습니다. 당신이하는 경우 에만 제네릭 컬렉션 항목을 당겨, 그것은 프로듀서 그리고 당신은 사용해야합니다 extends; 항목 채우는 경우 에는 소비자이므로 super. 동일한 컬렉션으로 둘 다 수행하는 경우 extends또는을 사용하지 않아야합니다 super.


매개 변수로 사물의 모음을받는 메서드가 있지만 Collection<Thing>.

사례 1 : 컬렉션을 살펴보고 각 항목으로 작업을 수행하려고합니다.
그러면 목록이 생산자 이므로 Collection<? extends Thing>.

이유는 a Collection<? extends Thing>가의 모든 하위 유형을 보유 할 수 Thing있으므로 Thing작업을 수행 할 때 각 요소가 a처럼 작동하기 때문입니다. ( Collection<? extends Thing>런타임 에 컬렉션 특정 하위 유형을 알 수 없기 때문에 실제로에 아무것도 추가 할 수 없습니다 Thing.)

사례 2 : 컬렉션에 항목을 추가하고 싶습니다.
그러면 목록이 소비자 이므로 Collection<? super Thing>.

여기서 이유 는 실제 매개 변수화 된 유형이 무엇이든 상관없이 Collection<? extends Thing>, Collection<? super Thing>는 항상 a를 보유 할 수 있다는 Thing것입니다. 여기에서 a Thing를 추가 할 수있는 한 이미 목록에있는 내용은 신경 쓰지 않습니다 . 이것이 ? super Thing보장하는 것입니다.


컴퓨터 과학에서 이것 뒤에 숨은 원리는

  • 공분산 : ? extends MyClass,
  • 반공 변성 : ? super MyClass
  • 불변 / 비 변산 : MyClass

아래 그림은 개념을 설명해야합니다. 사진 제공 : Andrey Tyukin

공분산 대 반공 분산


PECS (생산자 extends및 소비자 super)

니모닉 → Get and Put 원리.

이 원칙은 다음과 같이 말합니다.

  • 구조에서 값을 가져 오는 경우에만 extends 와일드 카드를 사용하십시오.
  • 구조에 값을 입력하는 경우에만 슈퍼 와일드 카드를 사용하십시오.
  • 그리고 얻을 때와 넣을 때 와일드 카드를 사용하지 마십시오.

자바의 예 :

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

}

Liskov 대체 원칙 : S가 T의 하위 유형이면 T 유형의 개체는 S 유형의 개체로 대체 될 수 있습니다.

프로그래밍 언어의 유형 시스템 내에서 입력 규칙

  • 유형의 순서를 유지하는 경우 공변 (≤), 더 구체적인 유형에서 더 일반적인 유형으로 정렬합니다.
  • 이 순서를 반대로하면 반 변형 ;
  • 둘 다 적용되지 않는 경우 불변 또는 불변 .

공분산 및 반공 분산

  • 읽기 전용 데이터 유형 (소스)은 공변 일 수 있습니다 .
  • 쓰기 전용 데이터 유형 (싱크)은 반 변성 이 될 수 있습니다 .
  • 소스와 싱크로 작동하는 변경 가능한 데이터 유형은 변하지 않아야합니다 .

이 일반적인 현상을 설명하기 위해 어레이 유형을 고려하십시오. Animal 유형의 경우 Animal [] 유형을 만들 수 있습니다.

  • 공변 : Cat []은 Animal []입니다.
  • 반 변형 : Animal []은 Cat []이고;
  • invariant : Animal []은 Cat []이 아니고 Cat []은 Animal []이 아닙니다.

자바 예 :

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  

더 많은 예

경계 (즉, 어딘가로 향함) 와일드 카드 : 와일드 카드 에는 세 가지 유형이 있습니다.

  • In-variance / Non-variance : ?또는 ? extends Object- 무제한 와일드 카드. 모든 유형의 가족을 나타냅니다. 얻을 때와 둘 때 사용하십시오.
  • 공분산 : ? extends T(의 하위 유형 인 모든 유형의 계열 T)- 상한이 있는 와일드 카드 . T는 IS 상단 상속 계층 구조 - 대부분의 클래스는. 구조에서 값을 가져 오는extends 경우에만 와일드 카드를 사용하십시오 .
  • Contra-variance : ? super T(의 상위 유형 인 모든 유형의 계열 T)- 하한이 있는 와일드 카드 . T는 IS 낮은 상속 계층 구조 - 대부분의 클래스는. 용도 super만하면 와일드 카드를 넣어 구조에 값을.

참고 : 와일드 카드 ?0 또는 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 roducer extends, C onsumer를 기억하는 데 도움이되도록 만든 니모닉 장치입니다 super.

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>.

Note that generally you should only be using ? extends T and ? super T for the parameters of some method. Methods should just use T as the type parameter on a generic return type.


In a nutshell, three easy rules to remember PECS:

  1. Use the <? extends T> wildcard if you need to retrieve object of type T from a collection.
  2. Use the <? super T> wildcard if you need to put objects of type T in a collection.
  3. If you need to satisfy both things, well, don’t use any wildcard. As simple as that.

(adding an answer because never enough examples with Generics wildcards)

       // 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);
        }
    }

let's assume this hierarchy:

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

Let's clarify PE - Producer Extends:

List<? extends Shark> sharks = new ArrayList<>();

Why you cannot add objects that extend "Shark" in this list? like:

sharks.add(new HammerShark());//will result in compilation error

Since you have a list that can be of type A, B or C at runtime, you cannot add any object of type A, B or C in it because you can end up with a combination that is not allowed in java.
In practice, the compiler can indeed see at compiletime that you add a B:

sharks.add(new HammerShark());

...but it has no way to tell if at runtime, your B will be a subtype or supertype of the list type. At runtime the list type can be any of the types A, B, C. So you cannot end up adding HammerSkark (super type) in a list of DeadHammerShark for example.

*You will say: "OK, but why can't I add HammerSkark in it since it is the smallest type?". Answer: It is the smallest you know. But HammerSkark can be extended too by somebody else and you end up in the same scenario.

Let's clarify CS - Consumer Super:

In the same hierarchy we can try this:

List<? super Shark> sharks = new ArrayList<>();

What and why you can add to this list?

sharks.add(new Shark());
sharks.add(new DeadHammerShark());
sharks.add(new HammerSkark());

You can add the above types of objects because anything below shark(A,B,C) will always be subtypes of anything above shark (X,Y,Z). Easy to understand.

You cannot add types above Shark, because at runtime the type of added object can be higher in hierarchy than the declared type of the list(X,Y,Z). This is not allowed.

But why you cannot read from this list? (I mean you can get an element out of it, but you cannot assign it to anything other than Object o):

Object o;
o = sharks.get(2);// only assignment that works

Animal s;
s = sharks.get(2);//doen't work

At runtime, the type of list can be any type above A: X, Y, Z, ... The compiler can compile your assignment statement (which seems correct) but, at runtime the type of s (Animal) can be lower in hierarchy than the declared type of the list(which could be Creature, or higher). This is not allowed.

To sum up

We use <? super T> to add objects of types equal or below T in list. We cannot read from it.
We use <? extends T> to read objects of types equal or below T from list. We cannot add element to it.


Remember this:

Consumer eat supper(super); Producer extends his parent's factory


Covariance: accept subtypes
Contravariance: accept supertypes

공변 유형은 읽기 전용이고 반공 변 유형은 쓰기 전용입니다.


실제 사례 사용 (일부 단순화 포함) :

  1. 목록과 유사한 화물차가있는화물 열차를 상상해보십시오.
  2. 당신은 할 수 있습니다 넣어 화물이있는 경우화물 자동차에화물을 동일하거나 작은 크기의 화물 차보다를 =<? super FreightCarSize>
  3. 창고에 충분한 공간 (화물 크기 이상)이있는 경우 화물차에서화물을 내릴 수 있습니다 =<? extends DepotSize>

참고 URL : https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super

반응형