5.5 默认方法冲突
问题
一个类实现了两个接口,每个接口包含的默认方法相同,但实现不同。
方案
在类中实现方法。借由关键字 super,实现仍然可以使用接口提供的默认方法。
讨论
Java 8 支持在接口中使用静态和默认方法。默认方法提供由类继承的实现,这使得接口可以在不破坏现有类实现的情况下添加新的方法。
由于一个类可以实现多个接口,它既可能继承具有相同签名但实现不同的默认方法,也可能已包含自己的默认方法。
此时需要考虑以下三种情况。
- 如果类的方法和接口的默认方法发生冲突,则类的方法始终优先。
- 如果两个接口(其中一个接口是另一个的后代)发生冲突,则后代接口优先;如果两个类(其中一个类是另一个的后代)发生冲突,则后代类优先。
- 如果两个默认方法之间不存在继承关系,则类无法编译。
对于第三种情况,只需在类中实现方法即可,第三种情况将简化为第一种情况。
观察例 5-13 所示的 Company 接口和例 5-14 所示的 Employee 接口。
例 5-13 包含默认方法的
Company接口
- public interface Company {
- default String getName() {
- return "Initech";
- }
- // 其他方法
- }
关键字 default 将 getName 方法指定为默认方法,它提供一个返回企业名称的实现。
例 5-14 包含默认方法的
Employee接口
- public interface Employee {
- String getFirst();
- String getLast();
- void convertCaffeineToCodeForMoney();
- default String getName() {
- return String.format("%s %s", getFirst(), getLast());
- }
- }
Employee 接口同样定义了一个名为 getName 的默认方法,其签名与 Company 接口中的 getName 方法相同,但二者的实现不同。如例 5-15 所示,CompanyEmployee 类实现了 Company 和 Employee 两个接口,从而导致冲突。
例 5-15 最初的
CompanyEmployee类(无法编译)
- public class CompanyEmployee implements Company, Employee {
- private String first;
- private String last;
- @Override
- public void convertCaffeineToCodeForMoney() {
- System.out.println("Coding...");
- }
- @Override
- public String getFirst() {
- return first;
- }
- @Override
- Public String getLast() {
- return last;
- }
- }
由于 CompanyEmployee 类继承了与 getName 无关的默认方法,它无法编译。为解决这个问题,需要在 CompanyEmployee 类中添加用户自定义的 getName 方法,它将重写两个默认方法。
不过,借由关键字 super,仍然可以使用所提供的默认方法,如例 5-16 所示。
例 5-16 调整后的
CompanyEmployee类
- public class CompanyEmployee implements Company, Employee {
- @Override
- public String getName() { ➊
- return String.format("%s working for %s",
- Employee.super.getName(), Company.super.getName()); ➋
- }
- // 其余代码和之前一样
- }
❶ 实现 getName 方法
❷ 通过 super 访问默认实现
可以看到,CompanyEmployee 类中的 getName 方法根据 Company 和 Employee 接口定义的两个默认方法 getName 构建了一个 String。
最好的消息是,这与默认方法一样复杂。读者现在已经了解了如何处理默认方法冲突。
实际上,我们还要考虑一种极端情况。如果 Company 接口定义了 getName 方法但没有将其指定为 default(也不存在相应的实现,即 getName 成为抽象方法),那么当 Employee 接口也定义了 getName 方法时,是否还会导致冲突呢?答案是肯定的。有趣的是,仍然需要在 CompanyEmployee 类中提供一个实现。
在 Java 8 之前,如果两个接口包含相同的方法且没有指定为默认方法,这并不会导致冲突,但类必须提供一个实现。
另见
有关接口中默认方法的讨论请参见范例 1.5。
