Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package ru.home.pecs.test04;
- import java.util.ArrayList;
- import java.util.List;
- // PECS - producer extend, consumer super
- class Plant {
- @Override
- public String toString() {
- return "I am a Plant !!";
- }
- }
- class Fruit extends Plant {
- @Override
- public String toString() {
- return "I am a Fruit !!";
- }
- }
- class Apple extends Fruit {
- @Override
- public String toString() {
- return "I am an Apple !!";
- }
- }
- class AsianApple extends Apple {
- @Override
- public String toString() {
- return "I am an AsianApple !!";
- }
- }
- /*
- // Inheritance diagram
- // наследование идет сверху вниз
- Plant
- ^
- |
- Fruit
- ^
- |
- Apple
- ^
- |
- AsianApple
- */
- public class App
- {
- public static void main(String[] args) {
- new App();
- }
- private void howTo() {
- // First read documents at
- // https://howtodoinjava.com/java/generics/java-generics-what-is-pecs-producer-extends-consumer-super/ //
- // or locally at
- // doc/Java Generics PECS - Producer Extends Consumer Super - HowToDoInJava.html
- // List of apples
- List<Apple> apples = new ArrayList<>();
- apples.add(new Apple());
- // Все ниже работает используя
- // subtype polymorphism or inclusion polymorphism
- // (В переменную базового класса можно пихать наследников, но не наоборот)
- // Здесь описаны ограничения внутри класса-generic
- // ========================================================
- // PRODUCER EXTENDS (UPPER BOUND) - корзина basket производит фрукты (с нашей точки зрения класса-generic)
- // We can assign a list of apples to a basket of fruits;
- // because apple is subtype of fruit
- // положим яблоки в крозину для фруктов basket1
- List<? extends Fruit> basket1 = apples;
- // basket1 объявлена как List<? extends Fruit> и это означает
- // что в basket1 может лежать наследники(и сам) Fruit, но точно какой наследник - заранее не известно,
- // поэтому компилятор не позволит обращаться к содержимому basket ниже(по диаграме) чем как к Fruit.
- // Here we know that in basket there is nothing but fruit(and upper)
- for (Fruit fruit : basket1){
- System.out.println(fruit);
- }
- // or plant
- for (Plant plant : basket1){
- System.out.println(plant);
- }
- // disallowed
- for (Apple apple : basket1){
- System.out.println(apples);
- }
- // disallowed
- for (AsianApple asianApple : basket1){
- System.out.println(asianApple);
- }
- // Этот предел называется верхний
- // (https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html)
- // И это верно для клиентского кода - использующего этот обобщенный класс.
- // Но при работе внутри generic-класса это утверждение инвертируется -
- // можно работать с интервалом классов, ограниченных Fruit снизу включительно.
- // <? extends Fruit> означает что мы можем внутри generic-класса
- // использовать вместо ? => [Plant, Fruit]
- //
- //
- // Весьма напоминает linux pipes (конвеер) |
- // Если мы берем вывод программы1 и перенаправляем его на вход программы2
- // program1 | program2
- // То это означает что данные которые были в stdout в 1 программе
- // попадают в stdin второй программы.
- // Такая же аналогия с InputStream / OutputStream
- //
- // Что это - входные или выходные данные, верхняя или нижняя граница -
- // все зависит, с какой стороны мы смотрим - со стороны клиента или generic-класса
- // корзина - производитель, она отказывается потреблять
- //disallowed all setters
- basket1.add(new AsianApple());
- basket1.add(new Apple());
- basket1.add(new Fruit());
- basket1.add(new Plant());
- // ========================================================
- // CONSUMER SUPER (LOWER BOUND) - корзина basket2 потребляет фрукты (берет у нас - с нашей точки зрения)
- // We can assign a list of apples to a basket of apples
- List<? super Apple> basket2 = apples;
- // disallowed all gets
- // корзина - потребитель, она отказывается производить
- //for (Plant plant : basket2){System.out.println(plant);}
- //for (Fruit fruit : basket2){System.out.println(fruit);}
- //for (Apple apple : basket2){System.out.println(apple);}
- //for (AsianApple asianApple : basket2){System.out.println(asianApple);}
- // Корзина basket2 объявлена как предок корзины List<Apple>,
- // Но заранее неизвестно, какой конкретно предок Apple будет использоваться
- // (клиентским кодом, который будет юзать наш generic-класс)
- // поэтому гарантированно в basket2 могут находиться классы начиная от Apple, заканчивая AsianApple
- // <? super Apple> означает что мы можем использовать вместо ? => [Apple, AsianApple]
- basket2.add(new Fruit()); //Compile time error
- basket2.add(new Plant()); //Compile time error
- basket2.add(new AsianApple()); //Successful
- basket2.add(new Apple()); //Successful
- }
- // Т.е. чтобы понять, с объектами какого типа мы можем работать (получать, передавать),
- // необходимо инвертировать определение границ подстановки ?
- // <? extends Class> - producer extends - можем обращаться к поставщику(supplier) за объектами [Object, Class]
- //
- // <? super Class> - consumer super - можем запихивать в потребителя объекты[Class, SubClass]
- // Еще теории
- // https://habr.com/ru/company/sberbank/blog/416413/
- // =============================================================================
- // Practice
- // =============================================================================
- // Практической применение:
- // -----------------------------------------------
- // метод потребляет яблоки клиентского кода
- static void consume(List<? extends Apple> basket) {
- // Здесь корзина производитель
- for(Fruit f : basket) {
- System.out.println(f);
- }
- }
- // метод снабжает клиентский код яблоками
- static void supply(List<? super Apple> basket) {
- //List<Apple> basket = new ArrayList<>();
- // Здесь корзина потребитель
- // disallowed
- basket.add(new Plant());
- // disallowed
- basket.add(new Fruit());
- basket.add(new Apple());
- basket.add(new AsianApple());
- }
- // Теперь перейдем к практическому использованию в клиентском коде:
- App() {
- // Тут
- // process(List<? extends Fruit> basket)
- // basket будет потребитель для наших яблок
- // basket принимает только Apple и выше - UPPER LIMIT - ограничение сверху
- // disallowed
- List<Plant> plants = new ArrayList<>();
- plants.add(new Apple());
- consume(plants);
- // disallowed
- List<Fruit> fruits = new ArrayList<>();
- fruits.add(new Apple());
- consume(fruits);
- List<Apple> apples = new ArrayList<>();
- apples.add(new Apple());
- consume(apples);
- List<AsianApple> asianApples = new ArrayList<>();
- asianApples.add(new AsianApple());
- consume(asianApples);
- // ==============================================
- List<Apple> basket = new ArrayList<>();
- supply(basket);
- // печатает все содержимое корзины
- basket.forEach(System.out::println);
- // я бы все это назвал с точки зрения клиентского кода
- // CEPS - consumer extends, producer super
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment