LINUX.ORG.RU

[Java] Вопрос по генерикам образовался

 


1

0

В Java есть подстановочные типы (wildcard type) и обобщенные классы (generic class). Первые выглядят примерно так <? [extends| super someClass]>, а вторые так <T [extends someClass]>.
По ходу у меня возникло несколько вопросов:
1) Насколько я понял из книжки Хорстмана они вообщем дублируют друг друга, позволяя писать независимый от типа код. Но не ясно какие у них отличия и в каких случаях лучше использовать подстановочные типы, а в каких обобщенные классы.
2) Зачем в случае подстановочных типов введены ограничения на супертипы <? super someClass> и подтипы <? extends someClass>, почему не реализовано аналогично «восходящему преобразованию» в обобщенных классах <T [extends someClass]> (т.е. почему нельзя читать и писать одной конструкцией)?
3) В разных изданиях Хорстманн описывает разное поведение подстановочных типов с ограничением на супертипы

For example, Pair<? super Manager> has methods

void setFirst(? super Manager)
? super Manager getFirst()
The compiler doesn’t know the exact type of the setFirst method but can call it with any object of type Manager, Employee, or Object, but not a subtype such as Executive.

Это противоречит его более раннему изданию, где в setFirst можно передавать Manager и Executive, но не их супертипы.
Одновременно у Эккеля аналогичная цитата:

the argument is now a List<? super T>, so the List holds a specific type that is derived from T; thus it is safe to pass a T or anything derived from T as an argument to List methods.

Кому, собственно верить?
4) Какая практическая польза от неограниченных подстановочных типов <?>? Я пока не вижу никакой, кроме проверки на null: с ними нельзя выполнять никаких других операций.

★★★★

Последнее исправление: cab (всего исправлений: 1)

Generic'и требуют обязательной типизации, т.е. если у тебя List<T extends Parent>, то T должен быть специфицирован при использовании. При этом если T объявлен как Parent, то список у тебя будет только List<Parent>, ему нельзя будет присвоить List<Child>

Wildcard'ы напротив не уточняются, т.е. если у тебя есть List<? extends Parent>, то ему можно присвоить и List<Parent>, и List<Child>

maxcom ★★★★★
()
Ответ на: комментарий от cab

это зависит от T. Если T это Child, то нельзя

maxcom ★★★★★
()
Ответ на: комментарий от maxcom

T должен быть специфицирован при использовании.

Что обозначает здесь слово «специфицирован» - не равен null?

cab ★★★★
() автор топика
Ответ на: комментарий от maxcom

Например так:

static <T extends Child> List<T> makeList1(T arg){
    List<T> result = new ArrayList<T>();
    result.add(arg);
    return result;
  }
?

cab ★★★★
() автор топика
Ответ на: комментарий от cab

С wildcard так бы не вышло, потому как тут тип определяется (неявно), а там на выходе так и остался бы wildcard

maxcom ★★★★★
()
Ответ на: комментарий от maxcom

Я это знаю. Я недопоня фразу «Он должен быть задан при объявлении типа» и привёл пример такого объявления, как я его понимаю. Можете привести примет Т, специфицированного при использовании?

cab ★★★★
() автор топика
Ответ на: комментарий от cab

Тут спецификация есть, но она не явная. Явная это, например, List<Child>.

Wildcard нужен когда мы хотим не прибивать тип гвоздями, например если у нас List<T>, то метод addAll у него объявлен не как addAll(Collection<T>), а как addAll(Collection<? extends T>), чтобы можно было передавать не только List<Parent>, но и List<Child>

maxcom ★★★★★
()
Ответ на: комментарий от cab

вот пример когда wildcard и generic'и совмещаются - функция поиска максимума в списке:

public static <T extends Comparable<? super T>> T max(List<? extends T> list)

она позволяет искать максимум типа T в коллекции которая содержит T или его ограничена любыми его потомками. При этом требуется чтобы T реализоввывал Comparable для себя или для какого-нибудь своего суперкласса.

maxcom ★★★★★
()
Ответ на: комментарий от maxcom

Если бы не требование реализации Comparable, то использование T extends не имело бы смысла? Достаточно было бы <? super T>. Так?

cab ★★★★
() автор топика
Ответ на: комментарий от cab

Wildcard используется чтобы делать интерфейсы более удобными, чтобы можно было передавать не точные генерики, а генерики определенные предками/потомками («producer-extends, comsuper-super» принцип). При этом wildcard нельзя использовать при создании объекта, wildcard не хорошо использовать в качестве типа возвращаемого значения.

maxcom ★★★★★
()
Ответ на: комментарий от maxcom

Да, я это понялю Спасибо.

cab ★★★★
() автор топика
Ответ на: комментарий от maxcom

Вы книжку Блоха имеете в виду? Я ее как раз и читаю, но до того раздела пока не добрался.

cab ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.