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

제네릭을 읽는 동안 PECS ( 생산자 extends와 소비자의super 약자 )를 만났습니다.

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



답변

tl; dr : “PECS”는 컬렉션의 관점에서 나온 것입니다. 당신이하는 경우 에만 제네릭 컬렉션 항목을 당겨, 그것은 프로듀서 그리고 당신은 사용해야합니다 extends; 당신이하는 경우 에만 항목을 채우고, 그것은 소비자 그리고 당신은 사용해야합니다 super. 당신이 모두 동일한 컬렉션 할 경우, 당신은 하나를 사용하지 않아야 extends또는 super.


매개 변수로 사물 모음을 취하는 메소드가 있다고 가정하지만을 수락하는 것보다 더 유연하게 만들고 싶다고 가정하십시오 Collection<Thing>.

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

추론은의 Collection<? extends Thing>모든 하위 유형을 보유 할 수 Thing있기 때문에 Thing작업을 수행 할 때 각 요소가로 작동 합니다. (실제로 컬렉션 의 특정 하위 유형이 보유하고 있음을 Collection<? extends Thing>알 수 없기 때문에 실제로는 아무것도 추가 할 수 없습니다 .)Thing

사례 2 : 컬렉션에 항목을 추가하려고합니다.
그런 다음 목록은 소비자이므로을 사용해야합니다 Collection<? super Thing>.

여기에서의 논리는 달리 있다는 것입니다 Collection<? extends Thing>, Collection<? super Thing>항상을 보유 수 Thing에 상관없이 실제 파라미터 화 된 형태가 무엇인지. 여기에 a Thing를 추가 할 수있는 한 이미 목록에있는 내용은 상관하지 않습니다 . 이것이 ? super Thing보장하는 것입니다.


답변

컴퓨터 과학에서 이것의 배후 원리는

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

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

공분산 vs.


답변

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

니모닉 → Get and Put 원칙.

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

  • 구조에서 값을 가져올 때만 확장 와일드 카드를 사용하십시오.
  • 구조에 값만 넣을 때는 슈퍼 와일드 카드를 사용하십시오.
  • 그리고 둘 다 넣을 때 와일드 카드를 사용하지 마십시오.

자바 예제 :

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 [] 유형을 만들 수 있습니다

  • 공변량 : 고양이 []는 동물 []입니다.
  • 반 변형 : 동물 []은 고양이 []입니다.
  • 불변 : 동물 []은 고양이 []가 아니며 고양이 []는 동물 []이 아닙니다.

자바 예제 :

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  

더 많은 예

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

  • 인 – 분산 / 비 분산 : ?? extends Object무제한으로 와일드 카드. 모든 유형의 가족을 나타냅니다. 둘 다 넣을 때 사용하십시오.
  • 공분산 : ? extends T(하위 유형의 모든 유형의 패밀리 T)- 상한이 있는 와일드 카드 . T는 IS 상단 상속 계층 구조 – 대부분의 클래스는. 사용 extends만 할 때 와일드 카드를 얻기 구조에서 값을.
  • 대조-분산 : ? 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 도움으로 조쉬 블로흐에 의해 생성 된 기억을 돕는 장치가 기억입니다 P의 roducer extends, C onsumer을 super.

이는 매개 변수화 된 유형이 메소드에 전달되면의 인스턴스를 생성T 할 때 (어떤 방식으로 검색) ? extends T서브 클래스의 모든 인스턴스 T가 또한 이므로 사용되어야 함을 의미 합니다 T.

파라미터 화 된 형태가하는 방법에 전달되는 경우 소비 의 인스턴스를 T(그들은 뭔가를 그에게 건네집니다) ? super T의 인스턴스 때문에 사용되어야한다T 일부 수퍼 타입을 허용하는 모든 메소드에 법적으로 전달할 수T . 예를 들어 Comparator<Number>에 A를 사용할 수 있습니다 Collection<Integer>. ? extends T에서 작동 Comparator<Integer>할 수 없기 때문에 작동하지 않습니다 Collection<Number>.

일반적으로 당신은 사용해야합니다 ? extends T? super T 일부 메소드의 매개 변수 및 매개 변수 . 메소드는 T일반 리턴 유형에서 유형 매개 변수로 사용해야 합니다.


답변

간단히 말해서 PECS를 기억하는 세 가지 쉬운 규칙 :

  1. 콜렉션에서 <? extends T>유형의 오브젝트를 검색해야하는 경우 와일드 카드를 사용하십시오 T.
  2. <? super T>유형의 객체를 넣어야하는 경우 와일드 카드를 사용하십시오.T컬렉션 .
  3. 두 가지를 모두 만족시켜야하는 경우에는 와일드 카드를 사용하지 마십시오. 저것과 같이 쉬운.

답변

이 계층 구조를 가정 해 봅시다 :

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 유형이 될 수있는 목록이 있기 때문에 java에서 허용되지 않는 조합으로 끝날 수 있으므로 A, B 또는 C 유형의 오브젝트를 추가 할 수 없습니다.
실제로 컴파일러는 컴파일 타임에 B를 추가하는 것을 실제로 볼 수 있습니다.

sharks.add(new HammerShark());

…하지만 런타임에 B가 목록 유형의 하위 유형 또는 상위 유형인지 여부를 알 수있는 방법이 없습니다. 런타임시 목록 유형은 A, B, C 유형 중 하나 일 수 있습니다. 따라서 예를 들어 DeadHammerShark 목록에 HammerSkark (슈퍼 유형)를 추가 할 수 없습니다.

* 당신은 말할 것입니다 : “하지만 가장 작은 타입이기 때문에 왜 HammerSkark를 추가 할 수 없습니까?”. 답변 : 그것은 가장 작은 당신 알고있다. 그러나 HammerSkark는 다른 사람에 의해 확장 될 수 있으며 같은 시나리오에서 끝납니다.

CS-Consumer Super를 명확히하자 :

동일한 계층에서 우리는 이것을 시도 할 수 있습니다 :

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)의 선언 된 유형과 계층 구조에서 높을 수있다. 이것은 허용되지 않습니다.

그러나 왜이 목록에서 읽을 수 없습니까? (요소를 가져올 수는 있지만 Object 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합니다 List. 읽을 수 없습니다.
우리 는 목록에서 <? extends T>유형이 같은 객체를 읽는 데 사용 T합니다. 요소를 추가 할 수 없습니다.