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

Другие вопросы по тегам