zoukankan      html  css  js  c++  java
  • 【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。

  • 相关阅读:
    Composite in Javascript
    Model Validation in Asp.net MVC
    HttpRuntime.Cache vs. HttpContext.Current.Cache
    Controller Extensibility in ASP.NET MVC
    The Decorator Pattern in Javascript
    The Flyweight Pattern in Javascript
    Model Binding in ASP.NET MVC
    Asp.net MVC
    jQuery Ajax 实例 全解析
    ASP.NET AJAX入门系列
  • 原文地址:https://www.cnblogs.com/h--d/p/14919885.html
Copyright © 2011-2022 走看看