默认方法
- 一旦类库设计者需要更新接口,向其中加入新的方法,继承该接口的代码就需要做出相应的修改。Java 8引入了新的解决方法,一是允许在接口中声明静态方法,二是引入默认方法,通过默认方法可以指定接口方法的默认实现,默认方法使用
default
修饰。
不断演进的API
不同类型的兼容性
- 变更对Java程序的影响大体可以分成三种类型的兼容性:
- 二进制级的兼容性,表示现有的二进制执行文件能无缝持续链接(包括验证、准备和解析)和运行。例如向接口添加一个方法就是二进制级的兼容,如果添加方法不被调用,那么现有程序就不会出现错误。
- 源代码级的兼容性,表示引入变化之后,现有的程序依然能成功编译通过。例如向接口添加新的方法就不是源代码级的兼容,因为遗留代码没有实现新引入的方法,所以无法顺利通过编译。
- 函数行为的兼容性,表示变更发生之后,程序接受同样的输入能得到同样的结果。还是例如向接口添加新的方法,是函数行为兼容的,因为新的方法没有调用,或者被实现覆盖而没有影响其表现。
默认方法的使用模式
可选方法
- 使用默认方法,可以为那些用户不会经常使用的,但是接口中包含的方法提供一个默认实现,这样实体类就无需在自己的实现中显示地提供一个空方法。
interface Iterator<T> {
boolean hasNext();
T next();
default void remove() {
throw new UnsupportedOperationException();
}
}
行为的多继承
- 类型的多继承:允许实现多个接口,而接口可以有默认实现,实质上实现了多继承。
- 用正交方法精简接口:分解实体类的不同功能点并设计接口,降低接口的重合度。
- 注意,继承不应该成为代码复用的万金油,例如继承一个100个方法的类就不是一个好选择,因为这会引入不必要的复杂性。可以使用代理模式有效的规避这类问题。
解决冲突的规则
解决为题的三条规则
- 类中方法的优先级最高。类或父类中声明的方法的优先级高于任何声明为默认方法的优先级。
- 如果1.无法判断,那么子接口的优先级最高。函数签名相同时,优先选择拥有最具体实现的默认方法的接口,如果
B
继承了A
,那么B
比A
更具体。
- 如果2.也无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法。
- 注意,上图中
D
未覆盖hello()
方法,但它实现了接口A
,那么它拥有A
的默认方法;此外,C
实现了接口B
,因此编译器会在接口A
和接口B
的hello()
之间选择(而不是类D
和接口B
之间)。由于B
更加具体,所以会选择接口B
的hello()
方法。
冲突及如何显式地消除歧义
- 上图中,编译器将无法判断哪一个接口的实现更加具体,此时应该使用显式地声明来确定使用哪一个方法,或者覆盖它。
public class C implements B, A {
void hello() {
B.super.hello();
}
}
菱形继承问题
- 上图中实际上只有一个方法可选,即接口
A
的默认方法。
- 此时接口
B
比接口A
更加具体,因此使用接口B
的hello()
方法。