Что такое 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) гласит, что “объекты в программе должны заменяться экземплярами их подтипов без изменения корректности этой программы”.
ограниченный (т. е. Направляющийся куда-то) подстановочный знак: Существует 3 разных варианта подстановочных знаков:
Совместное изменение: ? extends T (Правление T потомков) - подстановочный знак с верхней границей. T это самый верхний класс в иерархии наследования. Используйте extends подстановочный знак, когда вы только получаете значения из структуры. Верхние границы способствуют безопасной замене подтипов. Например, если T расширяет Animal , T может быть самим Animal или любым из его подтипов, таким как Cat, Dog и т.д. , Но это не может быть тип "выше", чем Animal , например Object .
Противопоставление: ? super T ( Царствование T предка) - подстановочный знак с нижней границей. T это самый нижний класс в иерархии наследования. Используйте super подстановочный знак, когда вы только помещаете значения в структуру. Нижние границы способствуют безопасной замене супертипов.
Разница / недифференцированность: ? или ? extends Object - Неограниченный подстановочный знак. Он обозначает семейство всех типов. Используйте, когда вы одновременно получаете и помещаете.
Примечание: подстановочный знак ? означает ноль или один раз, представляет неизвестный тип. Подстановочный знак может использоваться как тип параметра, никогда не используемый в качестве аргумента типа при вызове универсального метода, создании экземпляра универсального класса. (т. Е. При использовании подстановочного знака, который не используется нигде в программе, как мы используем T)
publicstaticvoidmain(String[] args) { //? extends Shape i.e. can use any sub type of Shape, here Shape is Upper Bound in inheritance hierarchy List<? extendsShape> intList5 = newArrayList<Shape>(); List<? extendsShape> intList6 = newArrayList<Cricle>(); List<? extendsShape> intList7 = newArrayList<Rectangle>(); List<? extendsShape> intList9 = newArrayList<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 = newArrayList<Shape>(); List<? super Shape> inList6 = newArrayList<Object>(); List<? super Shape> inList7 = newArrayList<Circle>(); //ERROR.
//----------------------------------------------------------- Circlecircle=newCircle(); Shapeshape= circle; // OK. Circle IS-A Shape
List<Circle> circles = newArrayList<>(); List<Shape> shapes = circles; // ERROR. List<Circle> is not subtype of List<Shape> even when Circle IS-A Shape
List<? extendsCircle> circles2 = newArrayList<>(); List<? extendsShape> shapes2 = circles2; // OK. List<? extends Circle> is subtype of List<? extends Shape>
//----------------------------------------------------------- Shapeshape2=newShape(); Circle circle2= (Circle) shape2; // OK. with type casting
List<Shape> shapes3 = newArrayList<>(); List<Circle> circles3 = shapes3; //ERROR. List<Circle> is not subtype of List<Shape> even Circle is subetype of Shape
List<? super Shape> shapes4 = newArrayList<>(); List<? super Circle> circles4 = shapes4; //OK. }
/* * Example for an upper bound wildcard (Get values i.e Producer `extends`) * * */ publicvoidtestCoVariance(List<? extends Shape> list) { list.add(newObject());//ERROR list.add(newShape()); //ERROR list.add(newCircle()); // ERROR list.add(newSquare()); // ERROR list.add(newRectangle()); // 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`) * */ publicvoidtestContraVariance(List<? super Shape> list) { list.add(newObject());//ERROR list.add(newShape());//OK list.add(newCircle());//OK list.add(newSquare());//OK list.add(newRectangle());//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. */ } }
publicvoidtestCoVariance(List<? extends B> myBlist) { Bb=newB(); Cc=newC(); myBlist.add(b); // does not compile myBlist.add(c); // does not compile Aa= myBlist.get(0); }
publicvoidtestContraVariance(List<? super B> myBlist) { Bb=newB(); Cc=newC(); myBlist.add(b); myBlist.add(c); Aa= myBlist.get(0); // does not compile } }