17

Для примера такой код:

public void add(List<? super Number> list) {
    list.add(1D); // можно
    list.add(new Object()); // нельзя
}

Почему так? Ведь здесь - <? super Number> - написано: "любой тип, являющийся супер-классом для Number". А работает всё наоборот: "любой тип - наследник Number". Что не так? Почему работает наоборот?

Regent
  • 19,134
Pavel
  • 5,327
  • 3
    http://ru.stackoverflow.com/questions/588385/%D0%94%D0%B6%D0%B5%D0%BD%D0%B5%D1%80%D0%B8%D0%BA%D0%B8-%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B0-wrong-2nd-argument-type. Здесь очень хорошо описана ответственность для super и extended – I. Perevoz Jan 11 '17 at 08:22
  • 3
    Еще может пригодиться этот вопрос: http://ru.stackoverflow.com/questions/361807 – Nofate Jan 11 '17 at 10:15

2 Answers2

15

Да, <? super Number> действительно подразумевает "любой тип, являющийся супер-классом для Number". Плюс сам Number.

По этой причине вы можете передать в метод, например, List<Object>.

Так как тип элементов в этом списке "что-то, являющееся суперклассом для Number", то можно добавлять в список элементы, тип которых является дочерним для Number. Например, Integer:

public void add(List<? super Number> list)
{
    list.add(1);
}

Это позволительно, так как в список можно добавлять элементы, тип которых является дочерним для типа элементов списка. Как в случае с обычным List<Number>:

List<Number> list = new ArrayList<>();
list.add(1);

Однако вы не можете добавить в список new Object(), потому что нет никаких гарантий того, что в списке можно хранить элементы типа Object: под ? может "скрываться" не только Object, но и, например, сам Number. В случае других классов (? super X), имеющих промежуточные классы между Object и самим X - ещё и любой промежуточный класс.

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


В случае <? extends Number> подразумевается "любой тип, являющийся дочерним классом для Number". Плюс сам Number.

В этом случае вы сможете передать в метод, например, List<integer>.

Так как тип элементов "что-то, являющееся дочерним классом для Number", то вы ничего не сможете добавить в список (кроме, разве что, null), ибо неизвестно что именно там может храниться.

А вот с полученным из списка элементом вы сможете работать как с Number:

public static void add2(List<? extends Number> list)
{
    int value = list.get(0).intValue();
}

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

Regent
  • 19,134
  • тоесть <? super Number> надо читать по факту как <? extends Number>?? Так получается? – Pavel Jan 11 '17 at 06:54
  • 1
    @Павел нет, не надо. Это две противоположности. super Number подразумевает родительские классы для Number (например, Object), а extends Number - дочерние классы (например, Integer). – Regent Jan 11 '17 at 06:59
  • 1
    я вот тоже не понял из ответа "почему?")) особенно прочитав в комментарии, что super Number подразумевает родительские классы для Number (например, Object) – Алексей Шиманский Jan 11 '17 at 07:06
  • Я тогда не понял ведь оно же так работает... Вы говорите: >>> Подобная конструкция говорит, что вы хотите работать со списком как со списком Number.<<< ту тогда и получается extends. А написано то super. Значит такая конструкция должна говорить "я хочу работать с элементами Number и Object - это и есть то что соответствует super. – Pavel Jan 11 '17 at 07:07
  • 1
    @Павел и в случае super, и в случае extends вы хотите работать со списком как со списком Number. Однако в случае super вы разрешаете передавать в метод List<Object>, ибо Object является суперклассом для Number. а в случае extends - разрешаете передавать List<Integer>, ибо он является дочерним классом для Number. – Regent Jan 11 '17 at 07:20
  • 1
    @АлексейШиманский переписал ответ. Надеюсь, теперь он понятнее и адресует большее количество вопросов, заданных автором в одном вопросе. И надеюсь, что нигде не опечатался и не ошибся. – Regent Jan 11 '17 at 08:12
  • 1
    @Павел переписал ответ и с информацией из комментариев, и с доп. информацией. Возможно, так будет понятнее. – Regent Jan 11 '17 at 08:13
5

Все очень просто.

Определение List'a с wildcard вида <? super Number> обозначает, что в списке хранятся элементы какого то типа от которого наследуется Number. Здесь не обязательно должен быть Object, там может быть и какой то промежуточный класс в иерархии наследования.

Например.
Есть классы A,B,C. A наследуется от B, B в свою очередь наследуется от C.
Если мы пишем List<? super A>, это значит что список может быть как List<A>,List<B> или List<C>. Соответственно, если придет List<B> то положить туда элементы типа C , будет ошибкой. От этой ошибки компилятор вас и предупреждает.

Artem
  • 14,967