Список <?> Или Список <Объект>

У меня есть метод, аргумент которого должен быть "List of anything". Метод не будет изменять содержимое списка. Правильнее ли определять этот метод как

void foo(List<?> list) {
}

или

void foo(List<Object> list) {
}

и в чем именно разница?

Ответы

Ответ 1

Короткий ответ, используя List<?>, вы сможете принять что-то вроде List<String> при использовании List<Object> не будет.

Это обсуждается в официальной тропе дженериков, здесь и здесь.

[...] здесь наивная попытка записать его с помощью generics (и нового для синтаксиса цикла):

   void printCollection(Collection<Object> c) {
       for (Object e : c) {
           System.out.println(e);
       }
   }

Проблема в том, что эта новая версия гораздо менее полезна, чем старая. В то время как старый код можно было вызывать с любым видом коллекции в качестве параметра, новый код принимает только Collection<Object>, который, как мы только что продемонстрировали, не является супертипом всех видов коллекций!
Итак, что такое супертип всех видов коллекций? Это написано Collection<?> (произносится как" коллекция неизвестных"), то есть коллекция, тип элемента которой соответствует чему-либо. Он вызвал подстановочный знак по очевидным причинам. Мы можем написать:

   void printCollection(Collection<?> c) {
       for (Object e : c) {
           System.out.println(e);
       }
   }

и теперь мы можем вызвать его с любым типом коллекции.

Ответ 2

Эти два совершенно разных

void foo(List<?> list)

означает, что вы ожидаете список чего-то, но вы не указали, что это такое

void foo(List<Object> list)

Говорит, что вы ожидаете, что представленный список - это список объектов, поэтому, если вы попытаетесь передать список, он не будет принят (в отличие от первого объявления)

Общей ошибкой является предположить, что List<String> является подтипом, так сказать, List<Object>, это не так, хотя String является suptype объекта.

Ответ 3

Если вы не хотите изменять список, лучше List<?>.

Таким образом, вы можете передать e. г. List<String> и List<BigInteger> к вашему методу.

Ответ 4

Вам нужно void foo(List<?> list), иначе вы не сможете, например, передать List<String>.

Если вы используете void foo(List<Object> list), вы можете пройти только List<Object>.

Это потому, что в Java дженерики не covariant: List<String> не является подтипом List<Object>.

Ответ 5

List<Object> имеет параметризованный тип Object, который по существу означает, что вы можете добавить в список все объекты.

Однако List<?> означает список неизвестных. Пока вы не собираетесь добавлять что-либо в список, это по сути то же самое, что и List<Object>, но вам придется беспокоиться об обстоятельствах следующим образом:

List<?> unknownList = new ArrayList<String>();
unknownList.add(new Object()); //Compilation error.

? (в данном случае) означает, что вы можете добавлять значения String или подтип String.

Если вы собираетесь перебирать список и извлекать значения, List<?> и List<Object> по существу одинаковы.

Подробнее здесь.

Ответ 6

В разделе 3 и 4 этого руководства вы должны ответить на ваш вопрос:

http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf

Цитата из учебника:

В общем случае, если Foo является подтипом (подклассом или подинтерфейсом) для Bar, а G является объявлением общего типа, это не тот случай, когда G <Foo> является подтипом G <Bar> ....

... Collection <Object> , который, как было продемонстрировано нами, не является супертипом всех видов коллекций! Итак, что такое супертип всех видов коллекций? Его письменная коллекция < > (произносится как "коллекция неизвестных" ), то есть коллекция, тип элемента которой соответствует чему-либо.

Однако в предположении strict, что метод foo оставит сбор немодифицированным, не должно быть различий в реализации.