Generic Wildcard Capture conversion Covariance Invariance Contravariance
От: Aleksei_Lekomtsev  
Дата: 19.10.23 13:45
Оценка:
class A {}
class B extends A {}
class C extends B {}

List<? extends B> list1 =
List<? super B> list2 =
List<B> list3 =


// чем отличаются list1, list2, list3 ?
// что можно положить в list1, list2, list3 ?
// что можно достать из list1, list2, list3 ?


В list3 можно присвоить(через =) только list<B>(инвариант)
В list2 можно присвоить(через =) только list<B> и list предков B, т.е. List<A>, List<Object>(ковариант)
В list1 можно присвоить(через =) только list<B> и list наследников B, т.е. List<C>(контрвариант)

Дальше сложнее,
в list1 через add ничего нельза добавить(Почему?)
В list2 через add можно добавить B и наследники B, т.е. С. Я так понимаю здесь работает capture conversion? Т.е. List<? super B> конвертируется в List<Object> или в List<B>,
поэтому можно добавлять B и его наследников, так как эти объектами будут как Object так и B?
В list3 через add можно добавить B и наследники B, т.е. С. Т.е. здесь тоже самое что и с List<? super B>? Или есть разница, что и когда выбирать?

в list1 через get можно читать B и A(Почему не C?)
В list2 через get можно читать Object?(Почему только Object)
В list3 через get можно читать B и A(В чем разница с List<? extends B>?)

Можно какие-то практические примеры когда какие типы более appropriate/fit?
Re: Generic Wildcard Capture conversion Covariance Invariance Contravariance
От: r0nd  
Дата: 20.10.23 00:34
Оценка: 1 (1)
Здравствуйте, Aleksei_Lekomtsev, Вы писали:

B>? Или есть разница, что и когда выбирать?


Это называется PECS — "Producer Extends, Consumer Super":

  1. Producer Extends — Если вам нужен List для получения значений типа T (вы хотите читать значения T из списка), вы должны объявить его с использованием <? extends T>, например, List<? extends Integer>. Однако вы не можете добавлять элементы в этот список.
  2. Consumer Super — Если вам нужен List для записи значений типа T (вы хотите записывать значения T в список), вы должны объявить его с использованием <? super T>, например, List<? super Integer>. Однако нет гарантий относительно типа объекта, который вы можете прочитать из этого списка.
  3. Если вам нужно как читать из списка, так и записывать в него, вы должны объявить его без использования подстановочных символов, например, List<Integer>.

Материалы по теме:


AL>Можно какие-то практические примеры когда какие типы более appropriate/fit?


RTFM
...<< Dementor 1.4.4 ✪ Lets Play a Game ⚂⚂⚃⚄⚄>>
Re: Generic Wildcard Capture conversion Covariance Invariance Contravariance
От: vsb Казахстан  
Дата: 20.10.23 02:24
Оценка: 1 (1)
Там же всё логично.

Ну вот взять List<? extends B> list1

? extends B это те типы, которые унаследованы от B, т.е. B или C или ещё что-то подобное. Какой именно тип — не указано.

Поэтому вопрос — почему add нельзя вызывать — он вроде очевиден. Мы не знаем, какой там тип. Может быть это List<B>. Может быть это List<C>. Может быть это ещё какой-то. Единственное, что мы знаем, что это не List<A> или List<Integer>. Ну и как тут что-то туда добавлять? Никак.

Что возвращает get() ? Ну объект какого-то класса, который унаследован от B. Какого — мы не знаем, но мы знаем, что мы можем прикастовать этот объект к B, он же от него унаследован. Поэтому с get() проблем нет.

Теперь возьмём List<? super B> list2. ? super B это какой-то из родительских классов. Может A, может Object. Поэтому add() работает без проблем — если наш объект хотя бы A, то никаких проблем нет. А вот с get — уже проблемки. Единственное, что точно знаем — что объекты в этом списке это наследники Object. Поэтому вызывать мы его можем, но типизации не дождёмся.

Ну и так далее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.