В чем разница между необработанными типами, неограниченными подстановочными знаками и использованием объекта в обобщениях - PullRequest
24 голосов
/ 09 сентября 2011

Я читаю главу об общих принципах в эффективной Java.

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

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

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

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

Ответы [ 8 ]

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

что означает "некий неизвестный тип"

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

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

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

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


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

3 голосов
/ 09 сентября 2011

Во время выполнения 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).Это означает, что вы не можете ничего добавить к этому набору (кроме нуля).Причина в том, что подстановочный знак ссылается на неизвестный тип.

2 голосов
/ 09 сентября 2011

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

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

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

1 голос
/ 08 января 2014

Я объяснял этот пункт моему другу и специально попросил метод 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);
}
1 голос
/ 09 сентября 2011
Set<?> set = new HashSet<String>();
set.add(new Object()); // compile time error

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

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

1 голос
/ 09 сентября 2011

Разница между 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, но лучше работает с системой универсальных типов.

0 голосов
/ 24 сентября 2016

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

  1. 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();
    }
    
  2. 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();
    }
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...