Здравствуйте.
Вот решил немного поглубже изучить Generic, так как возникла потребность написать свой класс. Раньше как-то не обращал внимание, а сейчас хочу разобраться.
Проблема в том, что не могу понять суть использования <T super SomeClass>
. Читал много литературы, но все-таки не понял.
Есть еще одна противоположная wildcard - <T extends SomeClass>
. С этим все очевидно. Мощная концепция. У нас есть суперкласс, в моем случае SomeClass
. Может быть и интерфейс. Но тут неважно. У него есть свои методы. Которые обязательно будут! Не важно, сколько раз был унаследован и какие методы добавил. Нам важно, что мы уверены в том, что у любого наследника класса будут методы суперкласса, и нам все равно, как называется класс-наследник, просто вызываем методы, и все. Тут все ясно.
НО вот <T super SomeClass>
что-то совсем не понятно, какие преимущества предоставляет. Я понимаю, что это противоположная вещь <T extends SomeClass>
, то есть просто один wildcard ограничивает сверху, другая снизу. Непонятно, зачем это использовать.
Читал про концепцию PECS. Примеры смотрел, но так и не понял.
Какой нам толк от того, что переданный класс будет суперклассом SomeClass
. Ну, допустим, Object
->ParentSomeClass
->SomeClass
будет такая иерархия. Ну, все классы валидно передадутся, и не будет compiler error. НО какой от этого толк? Я могу реализовать любые методы и в ParentSomeClass
, и в SomeClass
. Допустим, в ParentSomeClass
не будет методов, которые есть в SomeClass
. А в Object
, естественно, не будет методов, которые реализовал ParentSomeClass
. И какой тогда толк, одни проблемы.
Читал, что это используется, когда нужно добавлять элементы, в коллекцию, например. Но все-таки не понял сути.
Пожалуйста, объясните, как можно подробнее.
Чтобы понять мнемонику PECS (provider - extends, consumer - super), рассмотрим в качестве конкретного примера статический метод из стандартного класса Collections
:
public static <T> void copy(List<? super T> dest, List<? extends T> src)
Этот метод копирует список типа Т
в другой список. Он использует сразу оба вида ограничений: сверху для целевого списка и снизу для источника.
Начнем с источника. Источником могут быть любые дочерние от T
типы. Это логично, нас не интересует, что именно мы перекладываем, нужно, чтобы оно хотя бы сводилось к T. src
является для нас поставщиком (producer) объектов, и его тип мы позволяем расширять (extends). Это первые 2 буквы из мнемоники PECS.
Теперь перейдем к целевому списку. В дальнейшем этим списком кто-то будет пользоваться и, возможно, как-то его обрабатывать, и это накладывает определенные ограничения. Проще посмотреть это на примере.
Предположим, что у нас есть класс Питомцев
(являющийся подклассом Животных
) и дочерние классы Собачонок
и Котиков
:
class Animal { void feed() {} }
class Pet extends Animal { void call() {} }
class Kitty extends Pet{ void mew() {} }
class Doge extends Pet{ void bark() {} }
Теперь мы хотим скопировать из списка Питомцев
в список Питомцев
и позвать
их.
List<Pet> src = ...;
List<Pet> dest = new ArrayList<Pet>();
Collections.copy(dest, src);
for(Pet p: dest) p.call();
Пока все ок. А что если бы в src
были явно Котики
(унаследованные от Питомцев
)?
List<Kitty> src = ...;
List<Pet> dest = new ArrayList<Pet>();
Collections.copy(dest, src);
for(Pet p: dest) p.call();
Без проблем, Котики
откликаются на зов. При копировании в dest
мы "потеряем" знание о том, что это были именно Котики
, но по крайней мере они остаются Питомцами
и их все еще можно позвать.
Достаточно очевидно, что мы не можем скопировать Котиков
в коллекцию Собачонок
и заставить их всех лаять
:
List<Kitty> src = ...;
List<Doge> dest = new ArrayList<Doge>();
Collections.copy(dest, src);
for(Doge doge: dest) doge.bark();
Очевидный нонсенс, Котиков
нельзя просто грубо назвать Собачонками
, система типов не позволит так сделать, поэтому мы не смогли бы расширить тип первого параметра в сигнатуре метода copy
:
public static <T> void copy(List<? extends T> dest, List<? extends T> src); // так нельзя копировать!
Но что же насчет super
? Пусть наш код продолжит копировать Питомцев
, а где-то в другом место другой код кормит любых Животных
, и не только домашних.
List<Animal> dest = new ArrayList<Animal>();
.....
List<Kitty> src = ...;
Collections.<Pet>copy(dest, src);
....
for(Animal a: dest) a.feed();
Все Животные
накормлены. Заметим, что целевой список является коллекцией Животных
, являющихся родительским классом для Питомцев
. Общая концепция такова, что код, который использует в дальнейшем список dest
, не может в своих предположениях о его элементах опускаться ниже типа T
, но может сколь угодно абстрагироваться к родительским типам. Список dest
является потребителем (consumer), т.к. мы наполняем его и знаем, только, что его элементы совместимы с T
, а значит сами являются T
или лежат выше по иерархии (super). Это последние две буквы в мнемонике PECS.
Надеюсь, что-то прояснил для вас.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Есть сокеты на JavaВсе данные передаются в виде объектов(хотел как проще, а вышло как обычно)
Запускаю программу в режиме отладкиПрограмма выполняется, но в консоли надпись красным цветом:
Устанавливал jdk 12 и 8, потом удалил 8, но система запомнила только путь к 8