Список <?> Или Список <Объект>
У меня есть метод, аргумент которого должен быть "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 оставит сбор немодифицированным, не должно быть различий в реализации.