静态方法和默认方法
我们可以在 Comparator 接口的源码中, 看到大量类似下面这样的方法声明
- //default 关键字修饰的默认方法
- default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
- return thenComparing(comparingInt(keyExtractor));
- }
- //Comparator 接口中的静态方法
- public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
- return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
- }
其中 thenComparingInt() 就是一个默认方法, 它使用 default 关键字修饰. 这是 Java8 引入的新功能: 接口中可以声明默认方法和静态方法.
默认方法带来的多继承问题
在此之前, Java 中的类只支持多重继承, 不支持多继承. 现在有了默认方法, 你可以以另一种方式来实现类的多继承行为, 即一个类实现多个接口, 而这几个接口都有声明自己的默认方法.
这里面引发了一个多继承的问题, 设想一下, 假如一个类从多个接口中继承了它们声明的默认方法, 而这几个默认方法使用的都是相同的函数签名, 那么程序运行时, 类会选择调用哪一个方法呢?
代码清单一:
- @Test
- public void test2() {
- new C().hello();//result: hello from D
- }
- interface A {
- default void hello() {
- System.out.println("heelo from A");
- }
- }
- interface B extends A {
- default void hello() {
- System.out.println("heelo from B");
- }
- }
- class D implements A{
- public void hello() {
- System.out.println("hello from D");
- }
- }
- class C extends D implements A, B{
- }
代码清单一的输出结果是 hello from D, 可以看到, C 类的父类 D, 父接口 A, 父接口 B 都定义了一个相同函数签名的 hello() , 最后实际调用的是父类 D 中声明的方法.
代码清单二:
- @Test
- public void test4() {
- new I().hello();//result: heelo from G
- }
- class I implements G, H { }
- interface G extends E {
- default void hello() {
- System.out.println("heelo from G");
- }
- }
- interface H extends E { }
- interface E {
- default void hello() {
- System.out.println("heelo from E");
- }
- }
代码清单二的输出结果是 hello from G, 可以看到, I 类的父接口 G, 父接口 E 都定义了一个相同函数签名的 hello() , 最后实际调用的是父接口 G 中声明的方法.
代码清单三:
- @Test
- public void test3() {
- new F().hello(); //result: heelo from E
- }
- interface A {
- default void hello() {
- System.out.println("heelo from A");
- }
- }
- interface E {
- default void hello() {
- System.out.println("heelo from E");
- }
- }
- class F implements A, E {
- public void hello() {
- // 这里接口 A 和 E 不再具有继承关系, 需显式的选择调用接口 E 或 A 中的方法, 否则无法通过编译
- E.super.hello();
- }
- }
代码清单三中, 类 F 必须显式的覆盖父接口的 hello() 方法, 否则无法通过编译器的检测, 因为编译器无法确定父接口 A 和父接口 E 中的默认方法哪一个优先.
这种情况下, 如果你想调用某个父接口的默认方法, 可以使用 接口名. super. 默认方法名 这种方式进行调用.
总结
Java8 的新特性: 接口中可以声明默认方法和静态方法.
另外, 接口默认方法带来的多继承问题, 即如果一个类使用相同的函数签名从多个地方 (比如另一个类或接口) 继承了方法, 通过三条规则可以进行判断:
类中的方法优先级最高. 类或父类中声明的方法的优先级高于任何声明为默认方法的优先级.
如果无法依据第一条进行判断, 那么子接口的优先级更高: 函数签名相同时, 优先选择有最具体实现的默认方法的接口, 即如果 B 继承了 A, 那么 B 就比 A 更加具体.
最后, 如果还是无法判断, 继承了多个接口的类必须通过显式覆盖和调用期望的方法, 显式地选择使用哪一个默认方法的实现(调用语法: 接口名. super. 默认方法名 ).
来源: https://www.cnblogs.com/qingshanli/p/11774668.html