В чем разница между Collection<?> И Collection<T>
Я в основном разработчик C#, я преподавал структуры данных моему другу, и они используют Java в своем университете, и я видел такое выражение в Java:
void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}
Я не видел такой вещи в C#, поэтому мне интересно, в чем разница между Collection<T>
а также Collection<?>
на яве?
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
5 ответов
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
}
}
Декларация Collection<?>
(произносится как "коллекция неизвестного") является коллекцией, тип элемента которой соответствует чему-либо, тогда как Collection<T>
обозначает коллекцию типа T
,
Как обычно, в FAQ Анжелики Лангер по дженерикам подробно обсуждается эта тема, которую необходимо прочитать, чтобы полностью понять все дженерики в Java и, в частности, неограниченные символы подстановки (предмет этого вопроса). Цитата из FAQ:
Неограниченный подстановочный знак выглядит как "? " И обозначает семейство всех типов. Неограниченный подстановочный знак используется в качестве аргумента для создания экземпляров универсальных типов. Неограниченный подстановочный знак полезен в ситуациях, когда не требуется знание об аргументе типа параметризованного типа
Для получения дополнительной технической информации, ознакомьтесь с разделом §4.5.1 Аргументы типов и подстановочные знаки Спецификации языка Java, в котором говорится, что:
Аргументы типа могут быть ссылочными типами или подстановочными знаками. Подстановочные знаки полезны в ситуациях, когда требуется только частичное знание о параметре типа.
С Collection<T>
ты мог бы сделать
void printCollection(Collection<T> c) {
for (T e : c) {
System.out.println(e);
}
}
С Collection<?>
Вы только знаете, что коллекция содержит объекты.
Тот, который использует неограниченный подстановочный знак (?) На самом деле означает ? extends 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
, и не ?
,
Я мог бы, конечно, сделать то же самое с первым методом (используя ubounded подстановочный знак), но это не так чисто, я должен был предположить, что типом коллекции является объект, а не ?
(который нельзя использовать как аргумент типа: 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);
}
}
Я очень легко создал новую коллекцию типа T и скопировал все элементы исходной коллекции в эту вторую. Это я могу сделать, потому что я могу предположить, что тип коллекции — T, а не ?.
Я мог бы, конечно, сделать то же самое с первым методом (используя подстановочный знак ubounded), но это не так чисто, мне пришлось сделать предположения, что тип Collection — Object, а не ? (который нельзя правильно использовать в качестве аргумента типа: new ArrayList<?>()).