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

Разница между? (подстановочный знак) и параметр типа в Java

Может кто-нибудь объяснить мне, какая разница между этими двумя методами? Они такие же? Они выглядят одинаково с точки зрения того, что они решают. Если они одинаковы, зачем нужен ??

Метод # 1, неограниченный

public static void printList(List<?> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

Метод №2, Без ограничений:

public static <T> void printList(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

Метод № 1, ограниченный

public static void printList(List<? extends Number> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

Метод № 2, ограниченный:

public static <T extends Number> void printList(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}
4b9b3361

Ответ 1

Они одинаковы в том, что они принимают одни и те же типы параметров.

Однако идентификация типа с T (или любым другим) позволяет ссылаться на тип в другом месте.

Изменить: Примеры:

Ваши неограниченные примеры не в полной мере используют возможности параметризованных типов. У вас есть:

public static <T> void printList(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

И это достаточно для примера печати строковых представлений, но рассмотрим это (очень надуманное и без ошибок):

public static <T> T getSecondItem (List<T> list) {
    T item = list.get(1);
    return item;
}

Возвращаемый тип - это T, который позволяет безопасно выполнять такие действия, используя проверку типа времени компиляции:

class MyClass {
    public void myMethod () { }
}

void somewhere () {
    List<MyClass> items = ...;
    getSecondItem(items).myMethod();
}

Именованный тип также позволяет использовать однотипное ограничение в нескольких местах, например:

public <T> int compareLists (List<T> a, List<T> b) {
    ...
}

Если вы не указали тип, вы не можете указать ограничение, что a и b - это тот же тип списка (вы можете использовать List<? extends T> для большей гибкости).

Вы также спросили: "Зачем мне нужно ??". Реальный ответ: вы этого не делаете. Полагаю, это в основном для эстетики. Java стремится быть точным и беспорядочным языком. Есть много ситуаций, когда вам просто все равно, к какому типу вы обращаетесь. В таких случаях вы можете использовать ? без кода загромождения с объявлениями неиспользуемого типа.

Ответ 2

В ваших примерах нет абсолютно никакой разницы. Каждый из них выдаст тот же результат.

Лучшее использование и интерпретация генериков требует, чтобы вы знали семантику параметра типа, а также что-то о роли параметра. Причиной этого является то, что в случае, например, в вашем первом примере выше (неограниченная дикая карта) семантика представляет собой "список объектов неизвестного типа" и "параметр, который будет генерировать (не потреблять) List<?> экземпляры.

Метод выше просто создает каждый объект List<?> и вызывает toString() для каждого. Гарантируется, что у всех объектов есть метод toString(), поэтому нет необходимости вообще ничего знать о типе объекта для этой цели. Именно поэтому неограниченный шаблон является лучшим выбором для этого параметра метода: Чтобы создать экземпляры List<?> и вызвать toString() на них, нет необходимости ничего знать о типе объекта.

Обратите внимание, что если ? имеет одну и ту же семантику ( "Список объектов неизвестного типа" ), но другая цель ( "Список будет потреблять объекты неизвестного типа" ), все изменилось бы очень, очень радикально, так что может быть нецелесообразно (или очень сложно) использовать параметр подстановки (по крайней мере, без вспомогательного метода для захвата типа объекта).

Как правило, невозможно утверждать одну общую форму параметра для всех ситуаций. Наилучшая форма использования (подстановочный знак против конкретного, ограниченный или неограниченный; extends vs. super) зависит как от семантики параметра типа, так и от роли параметра в методе.

Ответ 3

Как упоминалось выше, в ? указано, что общий код не требует ссылки на тип. Как указано в Java Trails, это может быть класс, в котором свойства не зависят от любого типа, такого как длина коллекции.

Еще одна причина для ? заключается в том, что он предоставляет синтаксис для меньшей двусмысленности для случая, когда общий код требует только поведения, предоставляемого классом Object. Как указывалось выше, тип параметра <T> будет работать, но тот факт, что T был определен и никогда не использовался, может предположить, что разработчик ничего не заметил.

Итак, если <T> отбрасывается из-за семантической неоднозначности, а <?> недоступно, разработчику будет предложено написать <Object>. Однако это не позволяет использовать общий код для любого типа, просто тип объекта.

Итак, <?> более точно.