Java 接口中的可选方法

2022-08-31 08:13:27

根据我的理解,如果您用java实现一个接口,则该接口中指定的方法必须由实现所述接口的子类使用。

我注意到,在某些接口(如集合接口)中,有些方法被注释为可选,但这究竟是什么意思?它让我有点沮丧,因为我认为接口中指定的所有方法都是必需的吗?


答案 1

这里的答案似乎有很多混乱。

Java 语言要求接口中的每个方法都由该接口的每个实现实现。时期。此规则没有例外。说“收藏是一个例外”表明对这里真正发生的事情的理解非常模糊。

重要的是要意识到,符合接口有两个层次:

  1. Java语言可以检查什么。这几乎可以归结为:每种方法都有一些实现吗?

  2. 实际履行合同。也就是说,实现是否执行接口中的文档所说的应该执行的操作?

    编写良好的接口将包括文档,以准确解释实现的期望。编译器无法为您检查此项。您需要阅读文档,并按照他们说的去做。如果你不按照合约所说的去做,那么就编译器而言,你将有一个接口的实现,但它将是一个有缺陷的/无效的实现。

在设计集合 API 时,Joshua Bloch 决定,与其使用非常细粒度的接口来区分集合的不同变体(例如:可读、可写、随机访问等),他只会有非常粗糙的接口集,主要是 、和 ,然后将某些操作记录为“可选”。这是为了避免细粒度接口导致的组合爆炸。来自 Java Collections API Design FAQCollectionListSetMap

为了详细说明问题,假设您要将可修改性的概念添加到层次结构中。您需要四个新接口:ModifiableCollection,ModifiableSet,ModifiableList和ModefiableMap。以前是一个简单的层次结构,现在是一个混乱的异政府状态。此外,还需要一个新的迭代器接口,用于不可修改的集合,该接口不包含删除操作。现在,您可以取消“不支持的操作异常”吗?很遗憾,没有。

考虑数组。它们实现大多数 List 操作,但不进行删除和添加。它们是“固定大小”列表。如果要在层次结构中捕获此概念,则必须添加两个新接口:VariableSizeList 和 VariableSizeMap。您不必添加 VariableSizeCollection 和 VariableSizeSet,因为它们与 ModifiableCollection 和 ModifiableSet 相同,但为了保持一致性,您可能还是选择添加它们。此外,您需要一种不支持添加和删除操作的新品种ListIterator,以配合不可修改的列表。现在我们最多有十个或十二个接口,加上两个新的迭代器接口,而不是原来的四个。我们完成了吗?不。

考虑日志(例如错误日志、审核日志和可恢复数据对象的日志)。它们是自然的仅追加序列,支持除删除和设置(替换)之外的所有 List 操作。它们需要一个新的核心接口和一个新的迭代器。

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

现在我们多达二十个左右的接口和五个迭代器,几乎可以肯定的是,在实践中仍然存在一些不完全适合任何接口的集合。例如,Map 返回的集合视图是仅删除的自然集合。此外,有些集合会根据其值拒绝某些元素,因此我们仍然没有消除运行时异常。

当一切都说完了,我们觉得通过提供一组非常小的核心接口来回避整个问题,这可能会引发运行时异常,这是一个合理的工程妥协。

当集合 API 中的方法被记录为“可选操作”时,这并不意味着您可以在实现中省略方法实现,也不意味着您可以使用空的方法体(首先,其中许多方法需要返回结果)。相反,这意味着一个有效的实现选择(仍然符合契约的选择)是抛出一个不受支持的操作异常

请注意,因为 是 一 个 你可以从任何方法实现中抛出它,就编译器而言。例如,可以从 的实现中扔出它。但是,这样的实现将违反合同,因为 的文档没有说这是允许的。UnsupportedOperationExceptionRuntimeExceptionCollection.size()Collection.size()

题外话:Java的 Collections API使用的方法有些争议(然而,现在可能比它首次引入时要少)。在理想情况下,接口不会有可选操作,而是使用细粒度接口。问题在于Java既不支持推断的结构类型,也不支持交集类型,这就是为什么在集合的情况下,尝试以“正确的方式”做事最终会变得非常笨拙。


答案 2

为了编译接口的实现(非抽象)类 - 必须实现所有方法。

但是,如果我们将一个方法的实现是一个简单的异常抛出视为“未实现”(就像接口中的某些方法一样),那么在这种情况下,接口是异常,而不是常规情况。通常,实现类应该(并且将)实现所有方法。CollectionCollection

集合中的“可选”意味着实现类不必“实现”(根据上面的术语)它,它只会抛出NotSupportedException)。

一个很好的例子 - 不可变集合的方法 - 具体将只实现一个除了抛出之外什么都不做的方法add()NotSupportedException

在这种情况下,这样做是为了防止混乱的继承树,这将使程序员痛苦 - 但在大多数情况下,不建议使用这种范式,如果可能的话应该避免。Collection


更新:

从java 8开始,引入了一个默认方法

这意味着,接口可以定义一个方法 - 包括其实现。
添加此功能是为了允许向接口添加功能,同时仍支持不需要新功能的代码段的向后兼容性。

请注意,该方法仍由声明它的所有类实现,但使用接口的定义。