Подтвердить что ты не робот

В чем разница между 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

4b9b3361

Ответ 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);
    }
}