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


729

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

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


3
@ youtube.com/watch?v=34oiEq9nD0M&feature=youtu.be&t=1630 예제로 아주 좋은 설명이 super있지만 부분 을 설명 하지만 다른 아이디어를 제공합니다.
lupchiazoem

답변:


843

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보장하는 것입니다.


142
난 항상 그것에 대해 이런 식으로 생각하려고 해요 :의 생산자가 생산 뭔가 더 구체적으로 허용되고, 따라서 것은 확장 A, 소비자는 따라서,보다 일반적인 무언가를 받아들이도록 허용 슈퍼 .
Feuermurmel

10
생산자 / 소비자 구별을 기억하는 또 다른 방법은 메소드 서명을 생각하는 것입니다. 당신은 방법이있는 경우 doSomethingWithList(List list), 당신이 소모 목록을 그래서 공분산이 필요합니다 / 확장 (또는 불변 목록). 반면에 방법이 List doSomethingProvidingList인 경우 목록을 생성 하는 중이며 불균형 / 슈퍼 (또는 불변 목록)가 필요합니다.
Raman

3
@MichaelMyers : 왜 두 경우 모두에 매개 변수화 된 유형을 사용할 수 없는가? 여기에 와일드 카드를 사용하면 특별한 이점이 있습니까? 아니면 constC ++에서 메서드 매개 변수로 참조를 사용 하여 메서드가 인수를 수정하지 않음을 나타내는 것과 비슷한 가독성을 향상시키는 수단 입니까?
Chatterjee

7
@Raman, 나는 당신이 그것을 혼란스럽게 생각합니다. doSthWithList (List <? super Thing>을 가질 수 있음)에서 소비자이기 때문에 super (CS를 기억하십시오)를 사용할 수 있습니다. 그러나 List <? (PE)를 생성 할 때 더 구체적인 것을 반환 할 수 있기 때문에 Thing> getList ()를 확장합니다.
masterxilo

4
@AZ_ 나는 당신의 감정을 공유합니다. 메소드가 목록에서 get ()을 수행하면 메소드는 Consumer <T>로 간주되고 목록은 제공자로 간주됩니다. 그러나 PECS의 규칙은“목록의 관점에서”이므로 '확장'이 요구됩니다. GEPS 여야합니다. 슈퍼 넣어.
Treefish Zhang

561

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

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

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

공분산 vs.


143
안녕 모두들. 저는 Andrey Tyukin입니다. 저는 anoopelias & DaoWen이 저에게 연락하여 스케치 사용 허가를 얻었음을 확인하고 싶었습니다. Thx @ Anoop에게 두 번째 생명을주었습니다 ^^ @Brian Agnew : ( "투표 수") : 스칼라를위한 스케치이기 때문에 스칼라 구문을 사용하고 선언 사이트 차이를 가정합니다. 이는 Java의 이상한 호출과는 상당히 다릅니다 -사이트 차이 ... 어쩌면이 스케치가 Java에 어떻게 적용되는지 명확하게 보여주는 더 자세한 답변을 작성해야 할 것입니다.
Andrey Tyukin

3
이것은 내가 찾은 공분산 및 공분산에 대한 가장 간단하고 명확한 설명 중 하나입니다!
cs4r

@Andrey Tyukin 안녕하세요, 저는이 이미지를 사용하고 싶습니다. 어떻게 연락 할 수 있습니까?
8

이 그림에 대해 궁금한 점이 있으면 채팅방에서 토론 할 수 있습니다. chat.stackoverflow.com/rooms/145734/…
Andrey Tyukin


48

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.
        */  
    }
}

제네릭예제


이봐, 난 당신이 마지막 문장으로 무엇을 의미하는지 알고 싶었습니다. "비유가 잘못되었다고 생각되면 업데이트하십시오." 윤리적으로 잘못되었거나 (주관적) 프로그래밍 상황에서 잘못되었거나 (객관적 : 아니요, 잘못되지 않음) 의미합니까? 나는 그것을 문화적 규범과 윤리적 신념과는 독립적으로 보편적으로 받아 들일 수있는보다 중립적 인 예로 대체하고 싶습니다. 당신과 함께라면 괜찮습니다.
Neuron

마침내 나는 그것을 얻을 수 있었다. 좋은 설명입니다.
Oleg Kuts

2
@Premraj,, In-variance/Non-variance: ? or ? extends Object - Unbounded Wildcard. It stands for the family of all types. Use when you both get and put.List <?> 또는 List <?에 요소를 추가 할 수 없습니다. Object>를 확장하므로 왜 그런지 이해할 수 없습니다 Use when you both get and put.
LiuWenbin_NO.

1
@LiuWenbin_NO. -그 답의 일부는 오해의 소지가 있습니다. ?"제한되지 않은 와일드 카드"-불일치와 정확히 반대입니다. 다음 문서를 참조하십시오 : docs.oracle.com/javase/tutorial/java/generics/… 다음 내용 : 코드가 변수에 "in"및 "out"변수로 액세스해야하는 경우 와일드 카드를 사용하지 마십시오. ( "get"및 "put"과 동의어로 "in"및 "out"을 사용하고 있습니다). 를 제외하고는 null매개 변수화 된 컬렉션에 추가 할 수 없습니다 ?.
mouselabs

29
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
    }
}

따라서 "? extends B"는 "? B extends"로 해석되어야합니다. B 자체를 제외하고 B의 모든 슈퍼 클래스를 Object까지 포함하도록 B가 확장되는 것입니다. 코드 감사합니다!
Saurabh Patil

3
@SaurabhPatil 아니오, ? extends BB와 B를 확장하는 모든 것을 의미합니다.
asgs

24

내가 설명 할 것처럼 내 대답은 다른 질문에, 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일반 리턴 유형에서 유형 매개 변수로 사용해야 합니다.


1
이 원칙은 컬렉션에만 적용됩니까? 그것을리스트와 상관 시키려고 할 때 이치에 맞습니다. sort (List <T>, Comparator <? super T>) --->의 서명에 대해 생각하면 Comparator는 super를 사용하므로 PECS 컨텍스트의 소비자임을 의미합니다. 예를 들어 다음과 같은 구현을 살펴보면 다음과 같습니다. public int compare (Person a, Person b) {return a.age <b.age? -1 : a.age == b.age? 0 : 1; } 나는 Person이 아무것도 소비하지 않고 나이를 먹는 것처럼 느낍니다. 그것은 나를 혼란스럽게한다. 내 추론이나 PECS에 소장품에만 해당되는 결함이 있습니까?
Fatih Arslan

23

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

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

10

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

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합니다. 요소를 추가 할 수 없습니다.


9

(Generics 와일드 카드가 포함 된 예제가 충분하지 않기 때문에 답변 추가)

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

4

이것이 확장 대 슈퍼를 생각하는 가장 명확하고 간단한 방법입니다.

  • extends을위한 독서

  • super입니다 쓰기

"PECS"는 누가 "생산자"이고 누가 "소비자"인지에 대해 생각할 수있는 명백한 방법입니다. "PECS는"의 관점에서 정의 된 데이터 수집 자체 개체가 기록되는 경우 컬렉션 "소비하는"- (이 호출 코드에서 오브젝트를 소모)이,와 개체가 읽는 경우는 "생산" 에서 (이 그것을 호출 코드에 객체를 생성하는 중). 이것은 다른 모든 것이 어떻게 명명되는지에 반합니다. 표준 Java API는 콜렉션 자체가 아니라 호출 코드의 관점에서 명명됩니다. 예를 들어, java.util.List 의 콜렉션 중심 뷰 에는 "add ()"대신 "receive ()"라는 메소드 가 있어야합니다.요소이지만 목록 자체 요소를 받습니다 .

컬렉션과 상호 작용하는 코드의 관점에서 사물을 생각하는 것이 더 직관적이고 자연스럽고 일관성이 있다고 생각합니다. 코드가 컬렉션을 "읽거나"쓰나요? 그런 다음 컬렉션에 쓰는 코드 는 "제작자"가되고 컬렉션 에서 읽는 코드 는 "소비자"가됩니다.


나는 같은 정신적 충돌에 부딪 쳤고 PECS가 코드의 이름을 지정하지 않고 유형 경계 자체 Collection 선언에 설정되어 있다는 것을 제외하고는 동의하는 경향이 있습니다. 또한 이름 지정에 관한 한 종종 srcand와 같은 모음을 생성 / 소비하는 이름이 있습니다 dst. 그래서 당신은 코드와 컨테이너를 동시에 다루고 있으며, "컨테이너를 소비하는"코드와 소비하는 컨테이너를위한 "코드 생성"이라는 라인을 따라 생각했습니다.
mouselabs

4

PECS "규칙"은 다음을 합법적으로 보장합니다.

  • 소비자 : 무엇이든지간에 ?법적으로 참조 할 수 있습니다 T
  • 생산자 : 무엇이든지간에 ?법적으로 참조 할 수 있습니다. T

라인을 따라 일반적인 쌍을 이루는 List<? extends T> producer, List<? super T> consumer것은 컴파일러가 표준 "IS-A"상속 관계 규칙을 시행 할 수 있도록하는 것입니다. 우리가 합법적으로 그렇게 할 수 있다면, 말하기가 더 쉬울 수도 있습니다 <T extends ?>, <? extends T>(또는 위에서 볼 수 있듯이 Scala에서는 더 좋습니다) [-T], [+T]. 불행히도 우리가 할 수있는 최선은입니다 <? super T>, <? extends T>.

처음이 문제가 발생하여 머릿속에서 고장 났을 때 역학이 이해가되었지만 코드 자체는 계속 혼란스러워 보였습니다. 위의 내용은 명확했습니다. 표준 참조 규칙 준수를 보장하는 것입니다.

평범한 과제를 비유로 사용하여 그것을 보는 데 도움이되었습니다.

다음 (생산 준비가 아님) 장난감 코드를 고려하십시오.

// copies the elements of 'producer' into 'consumer'
static <T> void copy(List<? extends T> producer, List<? super T> consumer) {
   for(T t : producer)
       consumer.add(t);
}

위한 할당과 유사하게 측면에서 본를 도시 과제의 "좌측"- - 및 기준 와일드 (알 형)은 어떤 것을 보장 하고, "-A는" - 할당 될 수 있기 때문 은과 (와) 같은 유형 입니다.consumer?<? super T>?T?T?T

위해 producer우려 그냥 거꾸로 것 같습니다 : producer'의 ?와일드 카드 (알 수없는 타입)는 인 지시 대상 - 할당의 "오른쪽"- 그리고 <? extends T>어떤 보장 ?이다 ?"-A는" T- 있음 할당 할 수 있습니다 A를을T 때문에 ?같은 서브 타입 (또는 적어도 동일 유형)이다 T.


2

이것을 기억:

소비자는 저녁을 먹는다 (슈퍼); 프로듀서 , 부모 공장 확장


1

실제 예제 사용 (일부 단순화) :

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

1

공분산 : 서브 타입 동의
Contravariance을 : 슈퍼 타입을 수용

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


0

예를 보자

public class A { }
//B is A
public class B extends A { }
//C is A
public class C extends A { }

제네릭을 사용하면 안전한 방식으로 유형 작업을 동적으로 수행 할 수 있습니다

//ListA
List<A> listA = new ArrayList<A>();

//add
listA.add(new A());
listA.add(new B());
listA.add(new C());

//get
A a0 = listA.get(0);
A a1 = listA.get(1);
A a2 = listA.get(2);
//ListB
List<B> listB = new ArrayList<B>();

//add
listB.add(new B());

//get
B b0 = listB.get(0);

문제

Java의 Collection은 결과적으로 참조 유형이므로 다음 문제가 있습니다.

문제 # 1

//not compiled
//danger of **adding** non-B objects using listA reference
listA = listB;

* Swift의 generic에는 Collection이 Value type[About] 이므로 새 컬렉션이 만들어 지기 때문에 이러한 문제가 없습니다.

문제 # 2

//not compiled
//danger of **getting** non-B objects using listB reference
listB = listA;

솔루션-일반 와일드 카드

와일드 카드는 참조 유형 기능이며 직접 인스턴스화 할 수 없습니다

솔루션 # 1 <? super A> 일명 하한 (contravariance) 일명 소비자는 A와 모든 수퍼 클래스에 의해 운영됨을 보장하므로 추가 하는 것이 안전 합니다.

List<? super A> listSuperA;
listSuperA = listA;
listSuperA = new ArrayList<Object>();

//add
listSuperA.add(new A());
listSuperA.add(new B());

//get
Object o0 = listSuperA.get(0);

해결책 # 2

<? extends A>일명 상단은 안전 이유 즉, 그것은 모든 서브 클래스에 의해 작동되는 일명 공분산 일명 생산자 보증을 구속 얻을 캐스트

List<? extends A> listExtendsA;
listExtendsA = listA;
listExtendsA = listB;

//get
A a0 = listExtendsA.get(0);

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.