Java 什么';这是原始类型、无界通配符和在泛型中使用对象之间的区别

Java 什么';这是原始类型、无界通配符和在泛型中使用对象之间的区别,java,generics,effective-java,Java,Generics,Effective Java,我正在阅读关于有效Java中泛型的一章 请帮助我了解Set、Set和Set之间的区别 以下段落摘自此书 快速回顾一下,Set是一种参数化类型,表示 可以包含任何类型对象的集合,set是通配符类型 表示只能包含某些未知对象的集合 类型,Set是一个原始类型,它从泛型类型中选择 系统 “未知类型”是什么意思?类型对象的所有未知类型是否都是?在这种情况下,Set和Set之间的具体区别是什么?在Set和Set之间的区别是Set类型的变量可以指定一个更具体的泛型,如: Set<?> set =

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

请帮助我了解
Set
Set
Set
之间的区别

以下段落摘自此书

快速回顾一下,
Set
是一种参数化类型,表示 可以包含任何类型对象的集合,
set
是通配符类型 表示只能包含某些未知对象的集合 类型,
Set
是一个原始类型,它从泛型类型中选择 系统


“未知类型”是什么意思?类型
对象的所有未知类型是否都是
?在这种情况下,
Set
Set
之间的具体区别是什么?

Set
Set
之间的区别是
Set
类型的变量可以指定一个更具体的泛型,如:

Set<?> set = new HashSet<Integer>();
Set Set=newhashset();
Set
只能分配
Set

Set Set=newhashset();//不会编译
集合
仍然有用,因为任何对象都可以放入其中。从这个意义上讲,它很像原始的
集合
,但在泛型类型系统中工作得更好。

  • 原始类型(
    Set
    )将该类型视为完全没有泛型类型信息。请注意,这种微妙的影响不仅会忽略类型参数
    T
    ,还会忽略该类型的方法可能具有的所有其他类型参数。您可以向它添加任何值,它将始终返回
    Object
  • Set
    是一个
    集合
    ,它接受所有
    对象
    对象(即所有对象),并返回类型为
    对象
    的对象
  • Set
    是一个
    Set
    ,它接受某些特定但未知类型的所有对象,并将返回该类型的对象。由于对该类型一无所知,因此无法向该集合添加任何内容(除了
    null
    ),并且您只知道它返回的值是
    对象的某个子类型

设置
:这里没有泛型,不安全。添加您想要的内容


Set
:我们在范围内不知道的某种类型的集合。与运行时的
Set相同,由于类型擦除,JVM只会看到
Set

在编译时,有一个区别:

Set
使用
Object
参数化类型
E
,因此,
Set.add(E元素)
将参数化为
Set.add(Object元素)

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

Set Set=newhashset();
Set<?> set = new HashSet<String>();
set.add(new Object()); // compile time error
set.add(新对象());//编译时错误
因为我们不知道set的元素类型代表什么,所以我们不能添加对象 去吧。
add()
方法采用
E
类型的参数,即
集合的元素类型。
当实际类型参数为
时,它表示某个未知类型。任何 我们传递给add的参数必须是此未知类型的子类型。自从我们 不知道那是什么类型的,我们不能传递任何信息。唯一的例外是
null
,它是每种类型的成员

给定一个
集合
,我们可以调用
get()
并使用结果。结果类型是 未知类型,但我们始终知道它是一个对象。因此,安全地 将
get()
的结果分配给
Object
类型的变量,或将其作为参数传递 其中需要类型
对象

“未知类型”是什么意思

确切的意思是,
集合
有一些通用参数,但我们不知道它是什么

因此,分配给
集合
变量的集合可能是
集合
,或
集合
,或
集合
或包含任何其他特定类型的集合

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

如果您正在考虑向集合中添加某些内容,
add
方法将采用
(这很有意义,因为这是集合中的对象类型)。但是,如果您尝试添加任何特定对象,如何确保这是类型安全的?你不能-如果你正在插入一个字符串,你可能会把它放到一个
集合中,例如,这会破坏你从泛型中获得的类型安全性。因此,虽然您不知道泛型参数的类型,但不能提供这种类型的任何参数(除了
null
,因为这是任何类型的“实例”)



与大多数与泛型相关的答案一样,本文将重点放在集合上,因为它们更容易本能地理解。但是,参数适用于任何接受泛型参数的类-如果它是用无界通配符参数
声明的,则不能向它提供任何参数,并且您收到的任何类型的值都只能分配给
对象

我向我的朋友解释此项,并特别要求“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);
}
publicstaticvoidmain(字符串[]args){
列表字符串=新Arr
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);
}
// 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();
}
//  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();
}