В чем разница между Collection <?> И Collection <T>
Я в основном разработчик С#, и я преподавал Data Structures моему другу, и они используют Java в своем университете, и я видел такое выражение в Java:
void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}
Я не видел такой вещи в С#, поэтому мне интересно, какая разница между Collection<T>
и Collection<?>
в Java?
void printCollection(Collection<T> c) {
for (Object e : c) {
System.out.println(e);
}
}
Я думаю, что это могло быть написано и выше. Парень в документации сравнивал Collection<Object>
и Collection<T>
, хотя.
Примеры взяты из http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
Ответы
Ответ 1
Collection<?>
представляет собой набор неизвестных параметров типа.
Что касается вызывающего абонента, то нет разницы между
void printCollection(Collection<?> c) { ... }
и
<T> void printCollection(Collection<T> c) { ... }
Однако последнее позволяет реализации ссылаться на параметр типа коллекции и поэтому часто является предпочтительным.
Бывший синтаксис существует, потому что не всегда можно ввести параметр типа в правильной области. Например, рассмотрим:
List<Set<?>> sets = new ArrayList<>();
sets.add(new HashSet<String>());
sets.add(new HashSet<Integer>());
Если бы я заменил ?
на некоторый параметр типа T
, все наборы в sets
были бы ограничены одним и тем же типом компонента, т.е. я больше не могу ставить множества, имеющие разные типы элементов, в один и тот же список, о чем свидетельствует следующая попытка:
class C<T extends String> {
List<Set<T>> sets = new ArrayList<>();
public C() {
sets.add(new HashSet<String>()); // does not compile
sets.add(new HashSet<Integer>()); // does not compile
}
}
Ответ 2
Объявление Collection<?>
(произносится как "коллекция неизвестных" ) представляет собой коллекцию, тип элемента которой соответствует чему-либо, тогда как Collection<T>
обозначает коллекцию типа T
.
Как обычно, Angelika Langer Часто задаваемые вопросы по дженерикам содержит подробное обсуждение этой темы, обязательное чтение для полного понимания всех общих особенностей Java, и неограниченные подстановочные знаки (предмет этого вопроса) в частности. Цитата из FAQ:
Безграничный шаблон выглядит как "?" и обозначает семейство всех типов. Неограниченный подстановочный знак используется в качестве аргумента для экземпляров родовых типов. Неограниченный подстановочный знак полезен в ситуациях, когда не требуется знание о аргументе типа параметризованного типа
Для получения дополнительных технических подробностей ознакомьтесь с разделом §4.5.1 Типы аргументов и подстановочных знаков Спецификация Java Language, которая гласит, что:
Аргументы типа могут быть либо ссылочными типами, либо подстановочными знаками. Подстановочные знаки полезны в ситуациях, когда требуется только частичное знание параметра типа.
Ответ 3
С помощью Collection<T>
вы можете сделать
void printCollection(Collection<T> c) {
for (T e : c) {
System.out.println(e);
}
}
С Collection<?>
вы знаете, что коллекция содержит объекты.
Ответ 4
Тот, который использует неограниченный подстановочный знак (?), фактически означает ? extends Object
(все, что расширяет Object).
Это означает, что в Java подразумевается только для чтения, а именно, нам разрешено читать элементы из общей структуры, но нам не разрешено вносить что-либо в него, потому что мы не можем быть уверены в фактическом типе элементы в нем.
Поэтому я полагаю, что это очень действительный подход в обсуждаемом методе printCollection
, по крайней мере, до тех пор, пока мы не встретим ситуацию, в которой нам нужно принять тип.
Если бы пришлось выбирать между ними два, я бы сказал, что второй (с параметром типа T) является более чистым подходом, потому что вы можете хотя бы предположить, что коллекция имеет определенный тип T
, и это может доказать быть полезными в определенных сценариях.
Например, если сборка должна быть синхронизирована, я мог бы просто сделать:
<T> void printCollection(Collection<T> c) {
List<T> copy = new ArrayList<T>();
synchronized(c) {
copy.addAll(c);
}
for (T e : copy) {
System.out.println(e);
}
}
Я очень легко создал новую коллекцию типа T
и скопировал все элементы исходной коллекции в эту вторую. Это я могу сделать, потому что я могу предположить, что тип коллекции T
, а не ?
.
Я мог бы, конечно, сделать то же самое с первым методом (используя условный подстановочный знак), но он не настолько чист, мне приходилось делать предположения, что тип коллекции - это Object, а не ?
( который не может быть корректно использован как аргумент типа: new ArrayList<?>()
).
void printCollection(Collection<?> c) {
List<Object> copy = new ArrayList<Object>();
synchronized(c) {
copy.addAll(c);
}
for (Object e : copy) {
System.out.println(e);
}
}