PECS и WildCards на пальцах

Принцип PECS (Producer Extends Consumer Super) использует Wildcard — метод описания поискового запроса с использованием метасимволов (символов-джокеров).

Данная статья не научный труд. Это простое объяснение зачем введены понятия PECS и WildCards, что бы понять и запомнить.

4da360675210266341f8ebc40193731f.png

Для понимания WildCards необходимо четко представлять, что:

  • библиотечные контейнеры (коллекции) инвариантны — указателю, параметризированному классом Т, можно передать только контейнер параметризированный тем же классом Т;

  • библиотечные контейнеры хранят только ссылки (ни примитивы, ни строки, ни объекты они не хранят);

  • согласно принципам наследования в указатель типа Т можно передать экземпляр класса Т либо его наследников;

  • контейнер, параметризированный классом Т, хранит ссылки типа Т, которые ссылаются на адрес в памяти, в котором содержится либо экземпляр класса Т, либо экземпляр его наследника;

  • принцип PECS и WildCards созданы для лаконичности java-кодинга;

  • ограничения для WildCards имеют целью не допустить нарушения принципов наследования, а именно не допустить передачу экземпляра предка в указатель наследника.

Принцип применения WildCards для контейнеров в следующем:

  • в Consumer мы можем передать контейнеры предков класса (условно) T3 и самого класса T3

  • в Suplier мы можем передать контейнеры наследников класса (условно) T3 и самого класса T3

  • в Consumer мы будем иметь контейнеры с указателями на класс Т3 и его предков

  • в Suplier мы будем иметь контейнеры с указателями на класс Т3 и его потомков

  • в Consumer мы будем иметь указатели на Т3 и его предков, значит можем передать экземпляры Т3 и его потомков не нарушая принципов наследования.

  • в Consumer могут оказаться указатели от Т3 до Object, а указатели типа Object могут ссылаться на любые экземпляры иерархии Т. Как это получается:

    • указатели могут ссылаться на экземпляры Т3 и его потомков:

    • указатели могут ссылаться на экземпляры предков Т3, вплоть до Object

    • Следовательно, из Consumer без нарушения принципов наследования гарантированно можно получить экземпляр иерархии Т только в указатель типа Object. Затем эти экземпляры можно привести в соответствие к их классу явным приведением типов и передать в соответствующий указатель.

  • в Suplier мы будем иметь указатели на Т3 и его потомков. Теперь допустим, что на момент написания кода у Т3 имеется 4 наследника (Т4, Т5, Т6, Т7). Значит в Suplier может оказаться контейнер параметризованный от Т3 до Т7, содержащий указатели на экземпляры классов от Т3 до Т7. В такие указатели мы можем передавать экземпляры младшего класса в наследовании, т.е. Т7. Но мы не можем. Просто потому, что мы не можем ограничить наследование этих классов. Если разрешить передавать в Suplier экземпляры младшего на текущий момент класса с иерархии наследования, то со временем может возникнуть наследник Т8, которому ничто не запрещает быть переданным в Suplier, и который не сможет получить экземпляры класса Т7 в свои указатели, что нарушит принципы наследования. Поэтому в контейнер Suplier нельзя передавать элементы.

  • в Suplier могут оказаться указатели Т3 и ниже по иерархии, а указатели могут ссылаться так же на экземпляры Т и ниже по иерархии. Как это получается:

    • если в Suplier был передан контейнер параметризованный и содержащий указатели типа Т3 или его потомков, которые ссылаются на экземпляры Т3 или его потомков.

    • Следовательно, из Suplier без нарушения принципов наследования гарантированно можно получить экземпляр Т3 или ниже в иерархии в указатель Т3 или выше в иерархии. Затем эти экземпляры можно привести в соответствие к их классу явным приведением типов и передать в соответствующий указатель.

  • Из Consumer и из Suplier можно получить только содержащиеся в этих контейнерах элементы, получить в какой либо указатель содержащиеся в них контейнеры нельзя.

© Habrahabr.ru