H__D  

一、接口默认方法

  默认方法是在接口中的方法签名前加上了 default 关键字的实现方法

  • 代码示例如下:
 1 public class TestDefaultMethod {
 2     public static void main(String[] args) {
 3         ClasA a = new ClasA();
 4         a.foo();
 5     }
 6 }
 7 
 8 class ClasA implements InterfaceA {}
 9 
10 interface InterfaceA {
11     default void foo(){
12         System.out.println("InterfaceA foo");
13     }
14 }

 

  • 为什么要有默认方法

  在 java 8 之前,接口与其实现类之间的 耦合度 太高了(tightly coupled),当需要为一个接口添加方法时,所有的实现类都必须随之修改。默认方法解决了这个问题,它可以为接口添加新的方法,而不会破坏已有的接口的实现。这在 lambda 表达式作为 java 8 语言的重要特性而出现之际,为升级旧接口且保持向后兼容(backward compatibility)提供了途径。

二、默认方法的继承

  和其它方法一样,接口默认方法也可以被继承。

 1 interface InterfaceA {
 2     default void foo() {
 3         System.out.println("InterfaceA foo");
 4     }
 5 }
 6  
 7 interface InterfaceB extends InterfaceA {
 8 }
 9  
10 interface InterfaceC extends InterfaceA {
11     @Override
12     default void foo() {
13         System.out.println("InterfaceC foo");
14     }
15 }
16  
17 interface InterfaceD extends InterfaceA {
18     @Override
19     void foo();
20 }
21  
22 public class Test {
23     public static void main(String[] args) {
24         new InterfaceB() {}.foo(); // 打印:“InterfaceA foo”
25         new InterfaceC() {}.foo(); // 打印:“InterfaceC foo”
26         new InterfaceD() {
27             @Override
28             public void foo() {
29                 System.out.println("InterfaceD foo");
30             }
31         }.foo(); // 打印:“InterfaceD foo”
32         
33         // 或者使用 lambda 表达式
34         ((InterfaceD) () -> System.out.println("InterfaceD foo")).foo();
35     }
36 }

  接口默认方法的继承分三种情况(分别对应上面的 InterfaceB 接口、InterfaceC 接口和 InterfaceD 接口):

  • 不覆写默认方法,直接从父接口中获取方法的默认实现。

  • 覆写默认方法,这跟类与类之间的覆写规则相类似。

  • 覆写默认方法并将它重新声明为抽象方法,这样新接口的子类必须再次覆写并实现这个抽象方法。

接口继承行为发生冲突时的解决规则

 1 interface InterfaceA {
 2     default void foo() {
 3         System.out.println("InterfaceA foo");
 4     }
 5 }
 6  
 7 interface InterfaceB extends InterfaceA {
 8     @Override
 9     default void foo() {
10         System.out.println("InterfaceB foo");
11     }
12 }
13  
14 // 正确
15 class ClassA implements InterfaceA, InterfaceB {
16 }
17  
18 class ClassB implements InterfaceA, InterfaceB {
19     @Override
20     public void foo() {
21 //        InterfaceA.super.foo(); // 错误
22         InterfaceB.super.foo();
23     }
24 }

  当 ClassA 类多实现 InterfaceA 接口和 InterfaceB 接口时,不会出现方法名歧义的错误。当 ClassB 类覆写 foo 方法时,无法通过 InterfaceA.super.foo(); 调用 InterfaceA 接口的 foo方法。

  因为 InterfaceB 接口继承了 InterfaceA 接口,那么 InterfaceB 接口一定包含了所有 InterfaceA 接口中的字段方法,因此一个同时实现了 InterfaceA 接口和 InterfaceB 接口的类与一个只实现了 InterfaceB 接口的类完全等价。

接口与抽象类

  当接口继承行为发生冲突时的另一个规则是,类的方法声明优先于接口默认方法,无论该方法是具体的还是抽象的。

 1 interface InterfaceA {
 2     default void foo() {
 3         System.out.println("InterfaceA foo");
 4     }
 5  
 6     default void bar() {
 7         System.out.println("InterfaceA bar");
 8     }
 9 }
10  
11 abstract class AbstractClassA {
12     public abstract void foo();
13  
14     public void bar() {
15         System.out.println("AbstractClassA bar");
16     }
17 }
18  
19 class ClassA extends AbstractClassA implements InterfaceA {
20     @Override
21     public void foo() {
22         InterfaceA.super.foo();
23     }
24 }
25  
26 public class Test {
27     public static void main(String[] args) {
28         ClassA classA = new ClassA();
29         classA.foo(); // 打印:“InterfaceA foo”
30         classA.bar(); // 打印:“AbstractClassA bar”
31     }
32 }

  ClassA 类中并不需要手动覆写 bar 方法,因为优先考虑到 ClassA 类继承了的 AbstractClassA抽象类中存在对 bar 方法的实现,同样的因为 AbstractClassA 抽象类中的 foo 方法是抽象的,所以在 ClassA 类中必须实现 foo 方法。

三、接口静态方法

  Java 8 还在允许在接口中定义静态方法

 1 interface InterfaceA {
 2     default void foo() {
 3         printHelloWorld();
 4     }
 5     
 6     static void printHelloWorld() {
 7         System.out.println("hello, world");
 8     }
 9 }
10  
11 public class Test {
12     public static void main(String[] args) {
13         InterfaceA.printHelloWorld(); // 打印:“hello, world”
14     }
15 }

四、其他注意点

  • default 关键字只能在接口中使用(以及用在 switch 语句的 default 分支),不能用在抽象类中。

  • 接口默认方法不能覆写 Object 类的 equalshashCode 和 toString 方法。

  • 接口中的静态方法必须是 public 的,public 修饰符可以省略,static 修饰符不能省略。

  • 即使使用了 java 8 的环境,一些 IDE 仍然可能在一些代码的实时编译提示时出现异常的提示(例如无法发现 java 8 的语法错误),因此不要过度依赖 IDE。

 

posted on 2021-06-22 18:48  H__D  阅读(227)  评论(0编辑  收藏  举报