• 【Java】Java8新特性之接口默认方法与静态方法


    一、接口默认方法

      默认方法是在接口中的方法签名前加上了 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。

  • 相关阅读:
    android ListView布局之一(继承listActivity、使用arrayAdapter)
    android your project contains error
    wojilu系统的ORM代码解析[源代码结构分析,ObjectBase基类分析]
    ORM中启用数据库事务
    我记录网站综合系统 技术原理解析[11:ActionProcessor流程wojilu核心]
    互联网,让我们更安全了,还是更危险了【纯讨论】
    不用服务器也能跑的框架wojilu续篇
    使用wojilu 无代码实现 输入框提示 及其背后的原理
    wojilu日志系统可以单独使用
    “我有什么” 和 “你要什么” 框架制作的一些思考
  • 原文地址:https://www.cnblogs.com/h--d/p/14919885.html
Copyright © 2020-2023  润新知