Java集合框架API设计FAQ详细回答了这个问题:
问:为什么不在核心集合接口中直接支持不可变性,以便可以摆脱可选操作(和UnsupportedOperationException)?
答:这是整个API中最具争议的设计决策。显然,静态(编译时)类型检查非常有用,在Java中也是常见的。如果我们认为这是可行的,我们将会支持它。不幸的是,试图实现此目标会导致接口层次结构的爆炸,并且无法消除运行时异常的需要(尽管它们大大减少了)。
Doug Lea编写了一个流行的Java收集包,该包在其接口层次结构中反映了可变性差异,但基于用户对其收集包的使用经验,他不再认为这是一种可行的方法。用他的话来说(来自个人通信),“虽然我很难过地说,但强大的静态类型在Java中不适用于集合接口。”
为了详细说明问题,假设您想向层次结构添加可修改性的概念。您需要四个新接口:ModifiableCollection、ModifiableSet、ModifiableList和ModifiableMap。之前是一个简单的层次结构,现在是一个混乱的异层结构。此外,您需要一个新的Iterator接口,用于与不可修改的Collection一起使用,它不包含remove操作。现在您可以摆脱UnsupportedOperationException吗?不幸的是不能。
考虑数组。它们实现了大多数List操作,但不支持remove和add。它们是“固定大小”列表。如果您想在层次结构中捕获此概念,则必须添加两个新接口:VariableSizeList和VariableSizeMap。您不必添加VariableSizeCollection和VariableSizeSet,因为它们与ModifiableCollection和ModifiableSet相同,但出于一致性的考虑,您可能仍然选择添加它们。此外,您需要一种新的ListIterator类型,它不支持add和remove操作,以配合不可修改的List。现在我们已经有了十到十二个接口,加上两个新的Iterator接口,而不是最初的四个。我们完成了吗?没有。
考虑日志(例如错误日志、审计日志和可恢复数据对象的日志)。它们是自然的追加序列,支持除删除和设置(替换)之外的所有List操作。它们需要一个新的核心接口和一个新的迭代器。
那么不可变集合呢,与不可修改的集合相对应?(即,客户端无法更改的集合,并且永远不会因任何其他原因而更改)。许多人认为这是最重要的区别,因为它允许多个线程同时访问集合而无需同步。将此支持添加到类型层次结构中需要四个以上的接口。
现在我们已经有了二十个左右的接口和五个迭代器,几乎可以肯定仍然存在实际上不适合任何接口的集合。例如,Map返回的集合视图是自然的仅删除集合。此外,还有可能根据其值拒绝某些元素的集合,因此我们仍然没有摆脱运行时异常。
当一切都说完后,我们认为通过提供一组非常小的核心接口来抛出运行时异常是一个合理的工程妥协。
简而言之,拥有类似
Set
的接口和可选操作是为了防止需要大量不同接口而导致指数级爆炸。这并不像只有“不可变”和“可变”那么简单。Guava的
ImmutableSet
随后必须实现
Set
以与使用
Set
的所有其他代码进行互操作。虽然这并不理想,但确实没有更好的方法来解决它。
.supportsRemoval()
这样的方法。ImmutableSet
扩展了Set
,因为它指定了现在保证会抛出哪些方法。 - zapl