Java有default method
替代方案 Abstract Class
吗?我找不到使用默认方法而不是 Abstract 的真实案例?
4 回答
没有这样的计划,您可以通过比较已经记录的意图来得出这些计划,这与此类计划的含义不同:
主要目标是允许接口进化,即添加新方法。如果向接口添加新方法,则实现该接口的现有类将缺少实现,这将是不兼容的。为了兼容,实现必须来自某个地方,因此它是由默认方法提供的。
…</p>
Java 接口的主要目的是指定任何类都可以实现的协定,而无需更改其在类层次结构中的位置。确实,在 Java 8 之前,接口是纯粹抽象的。但是,这不是接口的基本属性。即使包含默认方法,其核心接口仍然指定实现类的协定。实现类可以覆盖默认方法,因此该类仍然可以完全控制其实现。(另请注意,默认方法不能是 final。)
Brian Goetz 写道:
向接口添加默认方法的最接近的原因是为了支持接口演化,……</p>
以下是一些完全符合设计目标的用例:
界面演变。在这里,我们正在向现有接口添加一个新方法,就该接口上的现有方法而言,该方法具有合理的默认实现。一个示例是将
forEach
方法添加到Collection
,其中默认实现是根据iterator()
方法编写的。“可选”方法。在这里,接口的设计者说“如果实现者愿意忍受所需要的功能限制,他们就不需要实现这个方法”。例如,
Iterator.remove
给定一个默认值 throwsUnsupportedOperationException
; 由于绝大多数的实现Iterator
无论如何都有这种行为,默认使这个方法本质上是可选的。(如果 from 的行为AbstractCollection
被表示为默认值Collection
,我们可以对可变方法执行相同的操作。)方便的方法。这些是严格为方便起见的方法,同样通常根据类上的非默认方法来实现。您的第一个示例中的
logger()
方法是对此的合理说明。组合器。这些是基于当前实例实例化接口的新实例的组合方法。例如,方法
Predicate.and()
或是Comparator.thenComparing()
组合器的示例。
请注意,这些并不针对抽象类的主要领域,例如提供骨架实现。除了技术差异之外,抽象类在语义上也不同,因为它们承担了关于如何实现功能的设计决策,哪些接口,即使是default
方法,也不应该。例如,一个众所周知的例子是List
接口,它存在两个根本不同的抽象类,AbstractList
并且AbstractSequentialList
选择子类或实现List
完全不同的类不应该被接口排除在外。所以List
接口定义了契约,永远不能替代抽象类,抽象类提供特定的基本实现。
其他答案和其他材料的链接已经充分涵盖了接口和抽象类之间的技术差异。没有很好地涵盖的是为什么要使用一个而不是另一个。
考虑在 Java 中使用类或接口的两种不同方式:作为调用者或作为子类。调用者有一个对象引用,并且可以通过该引用调用public
方法和访问public
字段。子类还可以访问、调用和覆盖protected
超类的成员。类可以有protected
成员,但接口不能。
一个常见的问题似乎是,既然我们有了默认方法,为什么我们需要抽象类?默认方法是接口呈现给调用者的一部分。protected
调用者无法使用类上的方法;它仅对子类可用。因此,如果您想与子类共享实现,请使用类(或抽象类)并定义protected
成员和字段。
该protected
机制允许类与子类通信,不同于它与调用者通信的方式。
但是 OP 提出了相反的问题:为什么要使用默认方法而不是抽象类?在您实际上可以选择的情况下(即,您的抽象不需要状态或受保护的方法,或者抽象类具有接口不需要的任何东西),具有默认方法的接口的约束远小于抽象类. 您只能从一个类继承;您可以从许多接口继承。因此,具有默认方法的接口可以表现得像无状态特征或混合,允许您从多个接口继承行为。
鉴于接口和抽象类用于不同的目的,因此没有删除或替换任何东西的计划。
引入接口中的默认方法的原因之一是允许向 JDK 接口添加新方法。
如果使用特定版本的接口编译类后,如果没有此功能,则无法向该接口添加新方法。使用接口中的默认方法,可以更改功能接口。