java集合框架接口中的UnsupportedOperationException


12

通过Java Collections Framework查看,我注意到相当多的接口都带有comment (optional operation)。这些方法允许类只通过UnsupportedOperationException不想实现的方法来实现。

这方面的一个示例是中的addAll方法Set Interface

现在,如本系列问题所述,接口是使用预期的定义合同。

接口很重要,因为它们将类的工作与工作方式分开。合同规定了客户的期望,使开发人员可以自由选择自己选择的方式实施合同,只要他们遵守合同即可。

界面是对对象可以执行的操作的描述……例如,当您打开电灯开关时,电灯就亮了,您不在乎如何做,只是它确实在做。在面向对象的程序设计中,接口是对象成为“ X”所必须具有的所有功能的描述。

我认为基于接口的方法要好得多。然后,您可以很好地模拟您的依赖关系,并且基本上所有事物之间的耦合都不太紧密。

接口的意义是什么?

什么是接口?

接口+扩展(mixin)与基类

鉴于接口的目的是定义一个契约并使您的依赖关系松散耦合,是否有些方法不能UnsupportedOperationException达到目的?这意味着我不能再通过了Set,只能使用addAll。相反,我必须知道Set我通过了什么实现,所以我可以知道是否可以使用addAll。对我来说,这似乎毫无价值。

那有什么意义UnsupportedOperationException呢?它只是在弥补遗留代码,并且需要清理其接口吗?还是我错过了更有意义的目的?


我不知道您使用的是哪个JRE,但是我的Oracle版本8没有addAll在中定义HashSet。它推迟到默认的实现AbstractCollection大多数肯定是不能UnsupportedOperationException

@Snowman你是对的。我误读了文档。我将编辑我的问题。
MirroredFate 2015年

1
我喜欢启动Eclipse并查看源代码,在代码引用和定义周围弹跳,以确保我正确使用了它。只要将JRE链接到src.zip它,就可以很好地工作。它有助于准确地知道JRE有时在运行什么代码,而不是遵循JavaDoc(可能有点冗长)。

Answers:


12

查看以下接口:

这些接口都将变异方法声明为可选方法。这隐含地证明了Collections类能够返回那些不可变的接口的实现这一事实:也就是说,这些可选的突变操作必定会失败。但是,根据JavaDoc中的合同,这些接口的所有实现都必须允许读取操作。这包括“正常”实现,如HashSetLinkedList以及在不可变的包装Collections

与队列接口对比:

这些接口指定任何可选操作:根据定义,队列旨在以FIFO方式提供和轮询元素。不变的队列与没有轮子的汽车一样有用。


反复出现的一个常见想法是拥有既具有可变对象又具有不可变对象的继承层次结构。但是,这些都有缺点。复杂性给水带来了麻烦,却没有真正解决问题。

  • 假设的对象Set可能具有读取操作,而子接口MutableSet可能具有写入操作。Liskov告诉我们,MutableSet然后可以将a 传递给任何需要a的对象Set。最初听起来不错,但是考虑一种方法,该方法期望底层集合在读取时不会被修改:两个线程可能会使用同一集合,并且违反不变的集合不变性。这可能会引起问题,例如,如果某个方法从集合中读取了两次元素,并且该元素是第一次但不是第二次出现。

  • Set可能没有直接实现,而是具有MutableSetImmutableSet作为子接口,然后用于实现类。这具有与上述相同的问题:在层次结构中的某个点,接口具有冲突的不变式。一个说“这个集合必须是可变的”,另一个说“这个集合不能改变”。

  • 对于可变和不可变的数据结构,可能有两个完全独立的层次结构。这增加了每吨什么结束是非常小的收益的额外的复杂性。这也具有不关心可变性的方法的特定弱点(例如,我只想迭代一个列表),现在必须支持两个单独的接口。由于Java是静态类型的,因此这意味着要使用额外的方法来处理两个接口层次结构。

  • 我们可以有一个接口,并允许实现在方法不适用时抛出异常。这是Java采取的路线,并且最有意义。接口的数量保持最少,并且不存在可变性不变性,因为记录的接口无论如何都不能保证可变性。如果需要不变性,请使用中的包装器Collections。如果某个方法不需要更改集合,则只需不对其进行更改。缺点是,如果从外部提供集合,则方法不能保证集合不会在另一个线程中更改,但是无论如何,这是调用方法(或其调用方法)所关心的。


相关阅读:Java 8为什么不包含不可变的集合?


1
但是,如果方法是可选的,则它们在接口中的位置是什么?例如,是否应该没有包含可选方法的单独接口MutableCollection
MirroredFate 2015年

不能。无法以任何有意义的方式在同一层次结构中具有可变和不可变的对象。最近有一个问题,有一个很好的图表显示了复杂性,并解释了为什么这是一个坏主意,但已将其删除。也许其他人知道一个问题可以帮助解释这一点,但我什么也找不到。但是,我将更新我的答案以进行一些解释。

这是关于不可变队列的广泛声明。我前几天用一个来解决这个问题
Karl Bielefeldt 2015年

@Snowman但这似乎使可变和不可变的对象彼此相反。我认为不可变对象实际上只是缺乏突变功能的对象。老实说,现在的方式更加复杂和混乱,因为您必须弄清楚什么是可变的实现,什么不是可变的实现。在我看来,将所有方法放在一个接口中与将可变方法拆分出来之间的唯一区别是一种清晰性。
MirroredFate

@MirroredFate阅读了我的最新编辑。

2

基本上是YAGNI。标准库中的所有具体集合都是可变的,实现或继承了可选操作。他们不在乎通用的不可变集合,绝大多数Java开发人员也不在乎。他们不会只为不可变的集合创建整个接口层次结构,然后不包括任何实现。

另一方面,有一些特殊用途的值或“虚拟”集合对于不可变性可能非常有用,例如empty setnCopies。此外,还有第三方不可变集合(例如Scala的集合),它们可能想调用现有的Java代码,因此它们使不可变集合以最小破坏性的方式打开的可能性保持不变。


好的,这很有道理。但是,似乎仍然从错误的方向解决了问题。为什么不从定义不可变的收集接口开始,然后为可变的收集实现定义可变的接口呢?
MirroredFate
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.