原始类型,无界外卡和generics中使用Object之间的区别是什么
我正在阅读有效Java中关于generics的章节。
帮我理解Set
, Set
和Set
之间的区别?
以下段落摘自本书。
作为快速回顾,
Set
是一个参数化类型,表示可以包含任何类型对象的Set
,Set
是一个通配符类型,表示一个只能包含某些未知类型的对象的Set
,而Set
是一个原始类型type,选择退出generics类型系统。
什么是“某种未知类型”? 是所有未知类型的Object
类型? 在这种情况下, Set
和Set
之间的具体区别是什么?
- 原始类型(
Set
)将类型视为根本没有generics类型信息 。 请注意细微的效果,不仅会忽略类型参数T
还会忽略该类型的方法可能具有的所有其他类型参数。 您可以向其添加任何值,它将始终返回Object
。 -
Set
是一个接受所有Object
对象(即所有对象)的Set
,它将返回Object
类型的Object
。 -
Set>
是一个Set
,它接受某些特定但未知类型的所有对象,并将返回该类型的对象。 由于对此类型一无所知 ,因此无法向该集合添加任何内容(null
除外),并且您唯一知道它返回的值是它们是Object
某个子类型。
在运行时,由于类型擦除,JVM将只看到Set
。
在编译时,有一个区别:
因此, Set
参数Set.add(E element)
带有Object
的类型E
,将Set.add(E element)
参数Set.add(Object element)
。
另一方面, Set>
,在类型E
上添加通配符,使Set.add(E element)
转换为Set.add(? element)
。 由于这不可编译,因此java将其“翻译”为Set.add(null element)
。 这意味着您无法向该集添加任何内容(null除外)。 原因是通配符引用了未知类型。
什么是“一些未知类型”的意思
究竟是什么意思 – Set
有一些通用参数,但我们不知道它是什么。
因此,分配给Set>
变量的Set
可能是Set
,或Set
,或Set
或包含任何其他特定类型的集合。
那么这对你如何使用它意味着什么呢? 那么,你从中获得的任何东西都将是一个实例?
无论那是什么。 由于我们不知道类型参数是什么,所以你不能说具体的东西比集合的元素可以赋值给Object
(只是因为所有类都从它扩展)。
如果你想在集合中添加一些东西 – 那么, add
方法需要一个?
(这是有道理的,因为这是集合中的对象类型)。 但是,如果您尝试添加任何特定对象,您如何确定这是类型安全的? 你不能 – 如果你正在插入一个String,你可能会把它放入一个Set
中,这会打破你从generics中获得的类型安全性。 因此,虽然您不知道generics参数的类型,但您不能提供此类型的任何参数(使用null
的唯一例外,因为它是任何类型的“实例”)。
与大多数与generics相关的答案一样,这一点集中在集合上,因为它们本能地更易于理解。 但是,参数适用于任何采用generics参数的类 – 如果使用无界通配符参数声明它?
,您不能向它提供任何参数,并且您收到的该类型的任何值只能分配给Object
。
Set
:这里没有generics,不安全。 添加你想要的。
Set>
:我们的范围中我们不知道的某种类型的集合。 与Set extends Object>
相同 Set extends Object>
。 可以引用任何类型的集合,但必须在实际实例化集合的位置定义该类型。 使用通配符引用,我们无法修改集合(我们不能添加或删除任何非null)。 就像一个观点。
Set
:包含对象的集合(仅基类,而不是子类)。 我的意思是你可以使用类型为Object的集合来实例化集合,例如HashSet
但不能使用HashSet
。 您当然可以将任何类型的元素添加到集合中,但仅仅因为它发生的一切都是Object或Object的子类。 如果将集合定义为Set,则只能添加Number的Numbers和子类,仅此而已。
Set
和Set>
之间的区别在于Set
Set>
类型的变量可以为其分配更具体的generics类型,如:
Set> set = new HashSet();
而Set
只能分配Set
:
Set
Set
仍然有用,因为任何对象都可以放入其中。 它就像那个意义上的原始Set
,但在generics类型系统中效果更好。
我正在向我的朋友解释这个项目,并特别要求“safeAdd”方法作为unsafeAdd示例的计数器。 所以在这里。
public static void main(String[] args) { List strings = new ArrayList (); 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 void safeAdd(List list, E o) { list.add(o); }
Set> set = new HashSet(); set.add(new Object()); // compile time error
由于我们不知道集合的元素类型代表什么,我们无法向其添加对象。 add()
方法接受类型为E
参数,即Set
的元素类型。 当实际的类型参数是?
,它代表一些未知的类型。 我们传递给add的任何参数都必须是这种未知类型的子类型。 因为我们不知道它是什么类型,所以我们无法传递任何内容。唯一的例外是null
,它是每种类型的成员。
给定Set>
,我们可以调用get()
并使用结果。 结果类型是未知类型,但我们始终知道它是一个对象。 因此,可以安全地将get()
的结果赋给Object
类型的变量,或者将其作为期望Object
类型的参数传递。
假设您正在编写一种常用方法来打印出现在List中的元素。 现在,此方法可用于打印Integer,Double,Object或任何其他类型的列表。 你选择哪一个?
-
List
:如果我们使用这个,这将有助于我们只打印Object类型的元素。 它不适用于打印属于Double等其他类的元素。 这是因为Generic默认情况下不支持inheritance,需要使用’super’关键字明确指定。// Would only print objects of type 'Object' public static void printList(List
-
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(); }