Вопрос-ответ

What is PECS (Producer Extends Consumer Super)?

Что такое PECS (производитель расширяет Consumer Super)?

Я наткнулся на PECS (сокращение от Producer extends и Consumer super), читая о дженериках.

Кто-нибудь может мне объяснить, как использовать PECS для устранения путаницы между extends и super?

Переведено автоматически
Ответ 1

tl; dr: "PECS" - это с точки зрения коллекции. Если вы только извлекаете элементы из универсальной коллекции, это производитель, и вы должны использовать extends; если вы только загружаете элементы, это потребитель, и вы должны использовать super. Если вы делаете оба с одной и той же коллекцией, вам не следует использовать ни extends, ни super.


Предположим, у вас есть метод, который принимает в качестве своего параметра набор объектов, но вы хотите, чтобы он был более гибким, чем просто принятие Collection<Thing>.

Пример 1: вы хотите просмотреть коллекцию и что-то сделать с каждым элементом.
Тогда список представляет собой producer , поэтому вам следует использовать Collection<? extends Thing>.

Причина в том, что a Collection<? extends Thing> может содержать любой подтип Thing, и, таким образом, каждый элемент будет вести себя как a Thing при выполнении вашей операции. (На самом деле вы не можете добавить ничего (кроме null) в Collection<? extends Thing>, потому что вы не можете знать во время выполнения, какой конкретный подтип Thing содержит коллекция.)

Пример 2: вы хотите добавить что-то в коллекцию.
Тогда список является consumer , поэтому вам следует использовать Collection<? super Thing>.

Причина здесь в том, что в отличие от Collection<? extends Thing>, Collection<? super Thing> всегда может содержать Thing независимо от того, каков фактический параметризованный тип. Здесь вам все равно, что уже есть в списке, пока это позволяет добавить Thing; это то, что ? super Thing гарантирует.

Ответ 2

Принципы, лежащие в основе этого, в информатике называются


  • Ковариация: ? extends MyClass,

  • Контравариантность: ? super MyClass и

  • Инвариантность / недифференцированность: MyClass

Картинка ниже должна пояснять концепцию. Картинка предоставлена: Андреем Тюкиным

Ковариация против контравариантности

Ответ 3

PECS - это мнемоническое устройство в Java generics, которое помогает нам запомнить, какое ключевое слово использовать при указании границ параметров универсального типа.

1 Производитель расширяет:


  • Применяется к возвращаемым типам и переменным с верхними границами.

  • Представляет собой производителя значений.

  • Позволяет возвращать более конкретный подтип объявленного типа (ковариантный).

  • Ковариация - это иерархическое отношение, т.е. отношение "есть-а": Кошка как "есть-а" Животное.

2 Consumer Super:


  • Применяется к входным параметрам и переменным с нижними границами.

  • Представляет потребителя значений.

  • Позволяет принять более общий супертип объявленного типа (контравариантный).

  • Контравариантность Изменения отношения подтипов: Animal как "can hold" Cat.

3. Инвариантные / невариантные типы


  • (без ограничений) имеют фиксированные отношения и не извлекают выгоду из PECS.

  • Фиксированное соотношение, т. е. тип должен оставаться точно таким, как указано, без какой-либо гибкости в принятии подтипов или супертипов: т. е. Где фиксированы как входные, так и выходные типы.

Принцип подстановки Лискова (LSP) гласит, что “объекты в программе должны заменяться экземплярами их подтипов без изменения корректности этой программы”.

введите описание изображения здесь Изображение src

ограниченный (т. е. Направляющийся куда-то) подстановочный знак: Существует 3 разных варианта подстановочных знаков:


  • Совместное изменение: ? extends T (Правление T потомков) - подстановочный знак с верхней границей. T это самый верхний класс в иерархии наследования. Используйте extends подстановочный знак, когда вы только получаете значения из структуры. Верхние границы способствуют безопасной замене подтипов.
    Например, если T расширяет Animal , T может быть самим Animal или любым из его подтипов, таким как Cat, Dog и т.д. , Но это не может быть тип "выше", чем Animal , например Object .

  • Противопоставление: ? super T ( Царствование T предка) - подстановочный знак с нижней границей. T это самый нижний класс в иерархии наследования. Используйте super подстановочный знак, когда вы только помещаете значения в структуру. Нижние границы способствуют безопасной замене супертипов.

  • Разница / недифференцированность: ? или ? extends Object - Неограниченный подстановочный знак. Он обозначает семейство всех типов. Используйте, когда вы одновременно получаете и помещаете.

Примечание: подстановочный знак ? означает ноль или один раз, представляет неизвестный тип. Подстановочный знак может использоваться как тип параметра, никогда не используемый в качестве аргумента типа при вызове универсального метода, создании экземпляра универсального класса. (т. Е. При использовании подстановочного знака, который не используется нигде в программе, как мы используем 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.
*/

}
}

Еще примеры кодирования

Визуализация концепции

Ответ 4
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
}
}
java generics