Ява противоположность сомнений - PullRequest
2 голосов
/ 28 июля 2011

Я прочитал этот пост о контравариантности:

Давайте теперь введем еще один шаблон: ? super. Учитывая супертип B из тип A, тогда C<B> является подтипом C<? super A>:

List<Fruit> fruits = new ArrayList<Fruit>();
List<? super Apple> = fruits; 

но почему C<B> является подтипом, а не супертипом?

Ответы [ 4 ]

3 голосов
/ 28 июля 2011

List<? super Apple означает список некоторых неизвестных супертипов Apple. В такую ​​переменную вы можете поместить List<Apple>, List<Fruit> (при условии, что Fruit является супертипом Apple) или List<Object>, например.

В этом и заключается смысл супертипа: List<? super Apple> является более общим, чем List<Apple>.

  • В List<Apple> вы можете положить яблоки и ничего больше. Все, что вы получаете из списка, это яблоко.
  • В List<? super Apple> вы можете положить яблоки (больше ничего, так как вы не знаете точный тип). Все, что вы получаете из списка, является объектом, но мы не знаем, что это за объект (например, это может быть List<Object>).

Если мы используем List<? extends Apple>, это аналогично: это означает список некоторых неизвестных подтипов Apple. Это могут быть List<Apple>, List<GoldenDelicious>, List<GrannySmith>, List<Jonagold> и т. Д.

  • В List<Apple> вы можете положить яблоки (все виды яблок), и все, что вы из этого получаете, это яблоко.
  • В List<? extends Apple> вы ничего не можете положить - так как вы не знаете, какой подтип Apple фактически использовался. Но все, что мы из этого получаем, на самом деле - яблоко.

Мы можем сделать все и еще с List<Apple>, чем с List<? super Apple> или с List<? extends Apple> - например, это подтип обоих подстановочных знаков.

(То же самое относится ко всем универсальным типам и их вариантам с подстановочными знаками, списки и похожие типы контейнеров легче увидеть).

2 голосов
/ 28 июля 2011

Полезным правилом для размышлений об иерархиях типов является принцип подстановки Лискова .В основном, это говорит о том, что A, являющийся подтипом B, означает, что вы можете использовать A везде, где вы можете использовать B. Обычно это не так.

Например, скажем, язлой король, который требует от своих подданных дань в виде куска Fruit.Можете ли вы принести мне экземпляр Apple?Да, конечно.Так что яблоко - это подтип фруктов.За горами есть другой злой король , который требует дани от своих подданных в виде Apple s.Можете ли вы принести ему Banana или какой-нибудь другой кусок Fruit?Нет!Он сделает так, чтобы тебя растоптали до смерти пони.

Итак, чтобы прожить долгую и счастливую жизнь в моем королевстве, вам понадобится запас Fruit.Вы можете хранить этот запас в List.Не могли бы вы сохранить его в List<Fruit>?Да, без проблем.Как насчет List<Apple>?Хорошо, потому что я приму Apple с.Таким образом, для целей производство , List<Apple> является подтипом List<Fruit>.Конечно, над горами в земле другого злого короля вы можете получить только List<Apple>, если только вы не хотите тратить много времени на приведение содержимого от List<Fruit> к Apple с, что, если только выВолшебник довольно утомительный.

Теперь, после каждого дня дани, у меня есть гора фруктов.Я даже не люблю фрукты, поэтому я должен положить их куда-нибудь.Очевидное место - List<Fruit>.Могу ли я вместо этого использовать List<Apple> здесь?Нет!Потому что некоторые из моих дорогих предметов могут принести мне Banana с, и я не смог бы поместить их в такой список.Однако над горами складывается иная ситуация.Злобный король интересуется только Apple с, поэтому он может с радостью использовать List<Apple> для хранения своего питательного запаса.Но может ли он использовать List<Fruit>?Да!Поскольку Apple - это Fruit, поэтому он может довольно легко поместить их в такой список.Это будет странно, когда он захочет вытащить их снова, но он может положить их довольно счастливо.Итак, с целью потребления мы видим, что кто-то, кто может использовать List<Apple>, может также использовать List<Fruit>, и поэтому мы имеем обратную ситуацию, что List<Fruit> является подтипом List<Apple>.Вот почему я не перехожу в другое злое царство.Забавное место.

Тем не менее, это лучше, чем Республика Хаскель.

2 голосов
/ 28 июля 2011

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

Т.е. вы должны использовать контравариантность, когда ваш List действует как потребитель, например:

public void fillWithApples(List<? super Apple> basket) {
    basket.add(new Apple());
}

List<Apple> basketOfApples = ...;
fillWithApples(basketOfApples);

List<Fruit> basketOfFruits = ...;
fillWithApples(basketOfFruits);

List<Object> basketOfAnything = ...;
fillWithApples(basketOfAnything);
0 голосов
/ 28 июля 2011

Если следовать правилу "Экземпляр подтипа class sub должен быть назначен везде, где ожидается экземпляр class supertype, это в значительной степени очевидно.

Расширьте его дальше - Представьте себе операцию, выполненную для List<? super A>, - Эта операция будет иметь смысл для List<B>, но не наоборот.

...