他们为什么决定让接口具有“可选操作”

问题描述:

ImmutableSet实现了Set接口。对ImmutableSet无意义的功能现在称为Set的“可选操作”。我假设这样的情况。因此ImmutableSet现在为许多可选操作抛出了UnsupportedOperationException他们为什么决定让接口具有“可选操作”

这似乎倒退给我。我被告知Interface是一个合约,以便您可以在不同的实现中使用强加功能。可选操作的方法似乎从根本上改变(矛盾?)接口意味着要做什么。今天实现这个,我会将Set接口分成两个接口:一个接口用于不可变操作,另一个接口扩展这些mutators操作。 (非常快,关闭袖口解决方案)

我知道技术的变化。我不是说应该这样或那样的。我的问题是,这种变化是否反映了Java的某些基础哲学的变化?它只是更多的东西,使事情向后兼容?我对接口有不完全的理解吗?

+1

*“接口是一个合约,因此您可以在不同的实现中使用强加功能” - 这不就是集合接口最终成功完成的事情吗?使用异常作为功能的一部分可能不受欢迎,但它是一种语言功能,正确使用或实现集合时没有任何可选的功能。你必须抛出/期待例外。尽管如此,我已经希望获得更薄的接口或至少像'.supportsRemoval()'这样的方法。 'ImmutableSet'扩展了'Set',它指定了现在保证抛出哪些方法。 – zapl 2014-10-30 19:46:56

Java Collections API Design FAQ回答这个问题详细:

问:你为什么不直接支持不可改变的核心集合接口,让您可以与可选的操作(和UnsupportedOperationException异常)做了吗?

答:这是整个API中最具争议性的设计决策。显然,静态(编译时)类型检查是非常可取的,并且是Java中的常态。如果我们相信这是可行的,我们会支持它。不幸的是,试图实现这个目标会导致接口层次的大小激增,并且不能成功地消除对运行时异常的需要(尽管它们大大减少了它)。

Doug Lea编写了一个广受欢迎的Java集合包,它的界面层次体现了可变性的区别,他不再相信这是一个可行的方法,基于用户体验他的集合包。用他的话来说(来自个人信件)“很多人说我很痛苦,强大的静态类型不适用于Java中的集合接口。”

为了说明血腥细节中的问题,假设您想要将可修改性的概念添加到层次结构中。您需要四个新的接口:ModifiableCollection,ModifiableSet,ModifiableList和ModifiableMap。以前简单的层次结构现在是一个混乱的层次结构。此外,您还需要一个新的Iterator接口,以便与不可修改的集合一起使用,该接口不包含删除操作。现在你可以抛弃UnsupportedOperationException了吗?不幸的是,考虑数组。考虑数组。他们实施大部分列表操作,但不能删除和添加。他们是“固定大小”列表。如果要在层次结构中捕获这个概念,则必须添加两个新接口:VariableSizeList和VariableSizeMap。您不必添加VariableSizeCollection和VariableSizeSet,因为它们与ModifiableCollection和ModifiableSet相同,但为了一致性,您可以选择添加它们。此外,您还需要一种不支持添加和删除操作的新ListIterator,以配合不可修改的List。现在我们有多达十个或十二个接口,再加上两个新的Iterator接口,而不是我们原来的四个接口。我们完了吗?考虑日志(例如错误日志,可恢复数据对象的审计日志和日志)。它们是自然附加序列,除了删除和设置(替换)以外,它们都支持所有的列表操作。他们需要一个新的核心接口和一个新的迭代器。

那么不可变的集合,而不是不可修改的集合呢? (即客户不能更改的集合,并且由于任何其他原因决不会改变)。许多人认为这是最重要的区别,因为它允许多个线程同时访问集合而不需要同步。将此支持添加到类型层次结构中需要四个接口。

现在我们可以使用二十个左右的接口和五个迭代器,而且几乎可以肯定的是,在实践中仍然存在一些集合,这些集合并不能完全适合任何接口。例如,Map返回的collection-views是自然的只删除集合。另外,有些集合会根据它们的值拒绝某些元素,所以我们仍然没有废除运行时异常。

当所有的事情都说完之后,我们觉得通过提供一小组可以抛出运行时异常的核心接口来回避整个问题是一种合理的工程折衷。

总之,具有类似于Set可选操作界面做是为了防止在需要不同接口的数量呈指数爆炸。它不像“不可变”和“可变”那么简单。然后,Guava的ImmutableSet必须实现Set以与所有其他使用Set的代码进行互操作。这并不理想,但实际上没有更好的办法。

+0

让集合扩展ImmutableCollection(或给它一个更好的名称,ReadableCollection)会不会更直观,以便更改集合的方法可以不在基本接口之外?事实证明,它打破了Liskov替代原则。 – 2014-10-30 15:58:28

+1

@ B.Dalton即使原则上可修改,由于类型,值或容量限制,或者Map返回的只删除集合视图,Collection仍将具有可选操作,如上所述。与此同时,ReadableCollection接口将是无用的:你不能依赖ReadableCollection是不可变的,因为它可能是一个可变的子类,并且你不能将它传递给其他代码,期望它们不能被它们修改,因为这样代码可以将其转换为Collection。所以分离不会实现任何事情。 – Boann 2014-10-30 16:19:21

+0

虽然我不知道更好的解决方案,但它似乎是接口(有?)的一个目的,它们将问题拖入错误的解决方案。你知道更好的解决这个问题的其他范例吗? – 2014-10-30 18:10:48