25

我正在阅读有效 Java 中的泛型一章。

帮助我理解和之间Set的区别?Set<?>Set<Object>

以下段落摘自书中。

快速回顾一下,Set<Object>它是一种参数化类型,表示可以包含任何类型的对象的集合,Set<?>是一种通配符类型,表示只能包含某种未知类型的对象的集合,并且Set是一种原始类型,它选择退出泛型类型系统。

“某种未知类型”是什么意思?都是未知类型的类型Object吗?Set<?>在那种情况下和之间的具体区别是Set<Object>什么?

4

8 回答 8

20
  • 原始类型 ( Set) 将该类型视为根本没有泛型类型信息。请注意,不仅类型参数T会被忽略,而且该类型的方法可能具有的所有其他类型参数都会被忽略。你可以给它添加任何值,它总是会返回Object
  • Set<Object>是一个Set接受所有Object对象(即所有对象)并将返回类型的对象Object
  • Set<?>是一个Set接受某些特定但未知类型的所有对象并将返回该类型的对象。由于对此类型一无所知,因此您无法向该集合添加null任何内容(除了),并且您对它返回的值唯一了解的是它们是Object.
于 2011-09-09T11:00:43.403 回答
3

Set在运行时,由于类型擦除,JVM 只会看到。

在编译时,有一个区别:

Set<Object>因此,参数化类型E将被参数化为.ObjectSet.add(E element)Set.add(Object element)

Set<?>另一方面,在类型上添加通配符,E因此Set.add(E element)被翻译为Set.add(? element). 由于这是不可编译的,因此 java 将其“翻译”为Set.add(null element). 这意味着您不能向该集合添加任何内容(null 除外)。原因是通配符引用了未知类型。

于 2011-09-09T11:03:19.257 回答
3

“某种未知类型”是什么意思

究竟是什么意思 -Set一些通用参数,但我们不知道它是什么。

因此,分配给Set<?>变量的集合可能是 a Set<String>,或 a Set<Integer>,或 aSet<Map<Integer, Employee>>或包含任何其他特定类型的集合。

那么这对您如何使用它意味着什么?好吧,你从中得到的任何东西都将是 的一个实例?,不管它是什么。因为我们不知道类型参数是什么,所以你不能说比集合的元素可以分配更具体的东西Object(只是因为所有类都从它扩展而来)。

如果您正在考虑向集合中添加一些东西 - 好吧,该add方法采用 a ?(这是有道理的,因为这是集合中对象的类型)。但是,如果您尝试添加任何特定对象,您如何确定这是类型安全的?你不能——如果你要插入一个字符串,你可能会把它放到一个Set<Integer>例子中,这会破坏你从泛型中获得的类型安全。因此,虽然您不知道泛型参数的类型,但您不能提供这种类型的任何参数(唯一的例外是null,因为它是任何类型的“实例”)。


与大多数与泛型相关的答案一样,这集中在集合上,因为它们更容易本能地理解。但是,这些参数适用于任何采用泛型参数的类——如果它是使用无界通配符参数声明的?,则您不能为其提供任何参数,并且您收到的该类型的任何值都只能分配给Object.

于 2011-09-09T16:38:32.900 回答
2

Set: 这里没有泛型,不安全。添加你想要的。

Set<?>: 一组我们从范围内不知道的特定类型。与 相同Set<? extends Object>。可以引用任何类型的集合,但该类型必须在集合实际实例化的地方定义。使用通配符引用,我们不能修改集合(我们不能添加或删除任何不为空的内容)。就像一个视图。

Set<Object>: 包含对象的集合(仅限基类,不包括子类)。我的意思是您可以使用 Object 类型的 Collections 来实例化该集合,例如HashSet<Object>但不能使用HashSet<String>. 您当然可以将任何类型的元素添加到集合中,但这只是因为碰巧一切都是 Object 或 Object 的子类。如果集合被定义为集合,则只能添加 Numbers 和 Number 的子类,仅此而已。

于 2011-09-09T11:00:50.653 回答
1

Set<Object>和之间的区别在于Set<?>,类型变量Set<?>可以分配更具体的泛型类型,如下所示:

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

Set<Object>只能分配Set<Object>

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

仍然有用,因为可以将Set<Object>任何对象放入其中。从这个意义上说,它很像原始Set的,但与泛型类型系统配合得更好。

于 2011-09-09T11:00:20.747 回答
1
Set<?> set = new HashSet<String>();
set.add(new Object()); // compile time error

因为我们不知道 set 的元素类型代表什么,所以我们不能向它添加对象。该add()方法接受 type 的参数E,即 的元素类型Set。当实际类型参数为?时,它代表某种未知类型。我们传递给 add 的任何参数都必须是这种未知类型的子类型。因为我们不知道那是什么类型,所以我们不能传入任何东西。唯一的例外是 null,它是每种类型的成员。

给定 a Set<?>,我们可以调用get()并使用结果。结果类型是未知类型,但我们始终知道它是一个对象。因此,将结果分配给get()类型变量Object或将其作为Object预期类型的​​参数传递是安全的。

于 2011-09-09T11:26:34.493 回答
1

我正在向我的朋友解释这个项目,并特别要求使用“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);
}
于 2014-01-08T04:13:04.657 回答
-1

假设您正在编写一个常用方法来打印出现在列表中的元素。现在,此方法可用于打印 Integer、Double、Object 或任何其他类型的列表。你选择哪一个?

  1. List<Object>: 如果我们使用这个,这将帮助我们只打印 Object 类型的元素。它对于打印属于其他类(如 Double)的元素没有用处。这是因为 Generic 默认不支持继承,需要使用 '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();
    }
    
于 2016-09-24T09:54:45.320 回答