제네릭을 읽는 동안 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
답변
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를 기억하는 세 가지 쉬운 규칙 :
- 콜렉션에서
<? extends T>
유형의 오브젝트를 검색해야하는 경우 와일드 카드를 사용하십시오T
. <? super T>
유형의 객체를 넣어야하는 경우 와일드 카드를 사용하십시오.T
컬렉션 .- 두 가지를 모두 만족시켜야하는 경우에는 와일드 카드를 사용하지 마십시오. 저것과 같이 쉬운.
답변
이 계층 구조를 가정 해 봅시다 :
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
합니다. 요소를 추가 할 수 없습니다.