原文
6.1.6 解决默认方法冲突
如果先在一个接口中将一个方法定义为默认方法,然后又在超类或另一个接口中定义了同样的方法,会发生什么情况?诸如 Scala 和 C++ 等语言对于解决这种二义性有一些复杂的规则。幸运的是,Java 的相应规则要简单得多。规则如下:
- 超类优先。如果超类提供了一个具体方法,同名而且有相同参数类型的默认方法会被忽略
- 接口冲突。如果一个接口提供了一个默认方法,另一个接口提供了一个同名而且参数类型相同的方法(不论是否是默认方法),必须覆盖这个方法来解决冲突。下面来看第二个规则。考虑两个包含 getName 方法的接口:
interface Person{
default String getName(){
reutrn "";
}
}
interface Named{
default String getName(){
return getClass().getName() + "_" + hashCode();
}
}
如果有一个类同时实现了这两个接口会怎么样呢?
class Student implements Person, Named{ ... }
这个类会继承 Person和 Named 接口提供的两个不一致的 getName 方法。并不是从中选择一个,Java 编译器会报告一个错误,让程序员来解决这个二义性问题。只需要在 Student 类中提供一个 getName 方法即可。在这个方法中,可以选择两个冲突方法中的一个,如下所示:
class Student implements Person, Named{
public String getName(){
return Person.super.getName();
}
}
现在假设 Named 接口没有为 getName 提供默认实现:
interface Named{
String getName();
}
Student 类会从 Person 接口继承默认方法吗? 这好像挺合理,不过,Java 设计者决定更强调一致性。两个接口如何冲突并不重要。如果至少有一个接口提供了一个实现,编译器就会报告错误,必须由程序员解决这个二义性。
注释:当然,如果两个接口都没有为共享方法提供默认实现,那么就与 Java 8之前的情况一样,这里不存在冲突。实现类可以有两个选择:实现这个方法,或者干脆不实现。如果是后一种情况,这个类本身就是抽象的。
我们只讨论了两个接口的命名冲突。现在来考虑另一种情况,一个类扩展了一个超类同时实现了一个接口,并从超类和接口继承了相同的方法。例如,假设 Person 是一个类,
Student 定义为:
class Student extends Person implements Named{ ... }
在这种情况下,只会考虑超类方法,接口的所有默认方法都会被忽略。在我们的例子中,Student 从 Person 继承了 getName 方法,Named 接口是否为 getName 提供了默认实现并不会带来什么区别。这正是“类优先”规则。
?“类优先”规则可以确保与Java 7的兼容性。如果为一个接口增加默认方法,这对于有默认方法之前能正常工作的代码不会有任何影响。
?警告:绝对不能创建一个默认方法重新定义 Object 类中的某个方法。例如,不能为toString或equals 定义默认方法,尽管对于List之类的接口这可能很有吸引力。由于“类优先”规则,这样的方法绝对无法超越 Object.toString 或 Objects.equals。
本文暂时没有评论,来添加一个吧(●'◡'●)