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

Какая разница между необработанными типами, неограниченными дикими картами и использованием объекта в дженериках

Я читаю главу о Generics in Effective Java.

Помогите мне понять разницу между Set, Set<?> и Set<Object>?

Следующий абзац взят из книги.

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

Что подразумевается под "неизвестным типом"? Все неизвестные типы типа Object? В этом случае какова конкретная разница между Set<?> и Set<Object>?

4b9b3361

Ответ 1

  • Необработанный тип (Set) относится к типу так, как если бы он вообще не имел общей информации типа. Обратите внимание на тонкий эффект, который игнорирует не только аргумент типа T, но и все другие аргументы типа, которые могут иметь методы такого типа. Вы можете добавить к нему любое значение, и оно всегда будет возвращать Object.
  • Set<Object> - это Set, который принимает все объекты Object (т.е. все объекты) и возвращает объекты типа Object.
  • Set<?> - это Set, который принимает все объекты определенного, но неизвестного типа и возвращает объекты этого типа. Поскольку об этом типе ничего не известно, вы не можете добавить ничего к этому набору (кроме null), и единственное, что вы знаете о возвращаемых значениях, это то, что они являются подтипом Object.

Ответ 2

Во время выполнения JVM просто увидит Set из-за стирания стилей.

Во время компиляции существует разница:

Set<Object> параметризовал тип E с помощью Object, поэтому Set.add(E element) будет параметризоваться до Set.add(Object element).

Set<?>, с другой стороны, добавляет подстановочный знак для типа E, поэтому Set.add(E element) переводится на Set.add(? element). Так как это не компилируется, java вместо этого переводит его как Set.add(null element). Это означает, что вы не можете добавить ничего к этому набору (кроме null). Причина в том, что подстановочный знак ссылается на неизвестный тип.

Ответ 3

что подразумевается под "неизвестным типом"

Точно, что это значит - Set имеет некоторый общий параметр, но мы не знаем, что это такое.

Таким образом, набор, назначенный переменной Set<?>, может быть Set<String> или Set<Integer> или Set<Map<Integer, Employee>> или набор, содержащий любой другой конкретный тип.

Итак, что это значит, как вы можете его использовать? Ну, все, что вы выберете из него, будет экземпляром ?, что бы это ни было. Поскольку мы не знаем, что такое параметр типа, вы не можете сказать ничего более конкретного, чем то, что элементы набора будут присваиваться Object (только потому, что все классы распространяются от него).

И если вы думаете добавить что-то в набор - ну, метод add принимает ? (что имеет смысл, так как это тип объектов внутри набора). Но если вы попытаетесь добавить какой-либо конкретный объект, как вы можете быть уверены, что это безопасно для типа? Вы не можете - если вы вставляете String, вы можете разместить его в Set<Integer>, например, что приведет к поломке типа безопасности, которое вы получаете от дженериков. Поэтому, пока вы не знаете тип общего параметра, вы не можете предоставить какие-либо аргументы этого типа (за единственным исключением null, как "экземпляр" любого типа).


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

Ответ 4

Set: здесь нет дженериков, небезопасных. Добавьте то, что вы хотите.

Set<?>: Набор определенного типа, которого мы не знаем из нашего объема. То же, что и Set<? extends Object>. Может ссылаться на Sets любого типа, но этот тип должен быть определен в точке, где набор фактически создается. С помощью подстановочной ссылки мы не можем изменить набор (мы не можем добавлять или удалять ни с чем, кроме нуля). Это похоже на представление.

Set<Object>: набор, содержащий объекты (только базовый класс, а не подклассы). Я имею в виду, что вы можете указать набор с помощью Collections типа Object, например HashSet<Object>, но не с HashSet<String>. Разумеется, вы можете добавлять в набор элементы любого типа, но только потому, что все это объект или подкласс объекта. Если набор был определен как Set, вы можете добавлять только числа и подклассы Number, и ничего больше.

Ответ 5

Разница между Set<Object> и Set<?> заключается в том, что переменная типа Set<?> может иметь для нее более специфичный общий тип, например:

Set<?> set = new HashSet<Integer>();

а Set<Object> может быть назначен Set<Object>:

Set<Object> set = new HashSet<Integer>(); // won't compile

Set<Object> по-прежнему полезен, поскольку в него может быть помещен любой объект. Это очень похоже на raw Set в этом смысле, но лучше работает с системой типового типа.

Ответ 6

Set<?> set = new HashSet<String>();
set.add(new Object()); // compile time error

Так как мы не знаем, что означает тип элемента set, мы не можем добавлять объекты к нему. Метод add() принимает аргументы типа E, тип элемента Set. Когда фактический параметр типа ?, это означает какой-то неизвестный тип. Любые параметр, который мы передаем для добавления, должен быть подтипом этого неизвестного типа. С тех пор, как мы не знаю, что это такое, мы ничего не можем передать. Единственным исключением является null, который является членом каждого типа.

Учитывая Set<?>, мы можем вызвать get() и использовать результат. Тип результата - это неизвестный тип, но мы всегда знаем, что это объект. Поэтому безопасно присвойте результат get() переменной типа Object или передайте ее как параметр где ожидается тип Object.

Ответ 7

Я объяснял этот вопрос своему другу и специально просил использовать метод safeAdd как счетчик примера unsafeAdd. Итак, вот оно.

public static void main(String[] args) {
    List<String> strings = new ArrayList<String>();

    unsafeAdd(strings, new Integer(42)); // No compile time exceptions

    // New 
    safeAdd(strings, new Integer(42)); // Throwing an exception at compile time


    String s = strings.get(0); // Compiler-generated cast

}

private static void unsafeAdd(List list, Object o) {
    list.add(o);
}


private static <E> void safeAdd(List<E> list, E o) {
    list.add(o);
}

Ответ 8

Скажем, вы пишете обычный метод для печати элементов, отображаемых в списке. Теперь этот метод можно использовать для печати списков типа Integer, Double, Object или любого другого типа. Какой из них вы выберете?

  • List<Object>: Если мы будем использовать этот, это поможет нам печатать только элементы типа Object. Это не будет полезно для печати элементов, принадлежащих другим классам, таким как Double. Это объясняется тем, что Generic не поддерживает Inheritance по умолчанию и должен быть явно задан с использованием ключевого слова "super".

    // Would only print objects of type 'Object'
    
    public static void printList(List<Object> list) {
        for (Object elem : list)
            System.out.println(elem + " ");
        System.out.println();
    }
    
  • List<?>: Это может помочь нам использовать общий метод печати любого типа данных. Мы могли бы использовать этот метод для печати экземпляров любого типа.

    //  The type would really depend on what is being passed
    public static void printList(List<?> list) {
        for (Object elem: list)
            System.out.print(elem + " ");
        System.out.println();
    }