Pull to refresh

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

Level of difficultyMedium
Reading time3 min
Views1.7K

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

Данная статья не претендует на научный труд. Тем не менее, для понимания требуется достаточно основательное знание языка Java. Ниже описаны минимальные знания, которыми читающий должен уверенно владеть.

При изучении рекомендуется иметь схему перед глазами в раскрытом виде.

Это лаконичное объяснение зачем введены понятия PECS и WildCards. Разобравшись "зачем и почему" запомнить "что и как" использовать уже не должно стать проблемой.

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

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

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

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

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

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

  • синтаксис WildCards;

    Обычный случай

    Инвариантный  List < T >

    Supplier

    Ковариантный List < ? Extends T >

    Consumer

    Контрвариантный List < ? Super T >

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

  • в PECS мы имеем дело с контейнерами, с хранящимися в контейнерах указателями, с хранящимися в указателях адресами экземпляров классов:

    • мы можем передавать контейнеры в Consumer или Supplier;

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

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

    • можем получать адреса экземпляров из указателей;

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

  • Для поясняющего примера принимаем, что:

    • имеется иерархия классов от Object до Т7 (см. схему выше)

    • Consumer и Supplier параметризованы классом Т3.

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

  • в Consumer

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

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

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

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

    • передавая в Consumer контейнеры, параметризованные классом выше Т3 в иерархии, мы получим в Consumer указатели на элементы Т2, Т1, Object.

  • итого в Consumer могут содержаться:

    • указатели типа Т3, Т2, Т1 или Object;

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

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

  • Затем эти экземпляры можно привести в соответствие к их классу явным приведением типов и передать в соответствующий указатель.

  • в Supplier

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

    • мы будем иметь указатели типа Т3 или его потомков;

    • мы будем иметь в указателях адреса экземпляров класса Т3 или его потомков;

    • нельзя передавать адреса экземпляров никаких классов;

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

  • итого в Supplier может содержаться:

    • указатели типа Т3, Т4, Т5, Т6, Т7 или ниже по иерархии... до бесконечности, пока не умрет код java;

    • адреса в этих указателях на экземпляры класса Т или ниже по иерархии;

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

  • Затем эти экземпляры можно привести в соответствие к их классу явным приведением типов и передать в соответствующий указатель.

Tags:
Hubs:
Total votes 2: ↑1 and ↓1+2
Comments3

Articles