1. 程式人生 > 其它 >【Java】Java8新特性之介面預設方法與靜態方法

【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類的equalshashCodetoString方法。

  • 介面中的靜態方法必須是public的,public修飾符可以省略,static修飾符不能省略。

  • 即使使用了 java 8 的環境,一些 IDE 仍然可能在一些程式碼的實時編譯提示時出現異常的提示(例如無法發現 java 8 的語法錯誤),因此不要過度依賴 IDE。