閱讀原文: Java8接口:靜態(tài)方法與默認(rèn)方法
往期文章一覽
Java8 接口更改包括接口中的靜態(tài)方法和默認(rèn)方法。在 Java8 之前,接口中只能有方法聲明,但是從 Java 8 開始,我們可以在接口中使用默認(rèn)方法和靜態(tài)方法。
Java 8 接口
設(shè)計接口一直是一項艱巨的工作,因為如果我們想在接口中添加其他方法,就需要在所有實現(xiàn)類中進(jìn)行更改。隨著接口的老化,實現(xiàn)它的類的數(shù)量可能會增長到無法擴(kuò)展接口的程度。這就是為什么在設(shè)計應(yīng)用程序時,大多數(shù)框架提供一個基本實現(xiàn)類,然后我們擴(kuò)展它并重寫適用于我們的應(yīng)用程序的方法。
讓我們看看默認(rèn)接口方法和靜態(tài)接口方法,以及它們在 Java8 接口更改中引入的原因。
Java 接口默認(rèn)方法
為了在 java 接口中創(chuàng)建默認(rèn)方法,我們需要在方法簽名中使用“default”關(guān)鍵字。例如:
package com.journaldev.java8.defaultmethod;
public interface Interface1 {
void method1(String str);
default void log(String str){
System.out.println("I1 logging::"+str);
}
}
注意,log(String str)是 Interface1 中的默認(rèn)方法。現(xiàn)在,當(dāng)一個類將實現(xiàn) Interface1 時,不必為接口的默認(rèn)方法提供實現(xiàn)。這個特性將幫助我們用額外的方法擴(kuò)展接口,我們只需要提供一個默認(rèn)的實現(xiàn)。
假設(shè)我們有另一個具有以下方法的接口:
package com.journaldev.java8.defaultmethod;
public interface Interface2 {
void method2();
default void log(String str){
System.out.println("I2 logging::"+str);
}
}
我們知道 Java 不允許我們繼承多個類,因為它會導(dǎo)致“菱形問題”,編譯器無法決定使用哪個超類方法。使用默認(rèn)方法時,接口也會出現(xiàn)菱形問題。因為如果一個類同時實現(xiàn)了 Interface1 和 Interface2 并且沒有實現(xiàn)公共的默認(rèn)方法,編譯器就不能決定選擇哪個方法。
擴(kuò)展多個接口是 Java 不可或缺的一部分,您可以在核心 Java 類以及大多數(shù)企業(yè)應(yīng)用程序和框架中找到它。因此,為了確保這個問題不會出現(xiàn)在接口中,必須為接口的常見默認(rèn)方法提供實現(xiàn)。因此,如果一個類同時實現(xiàn)上述兩個接口,則它必須為 log()方法提供實現(xiàn),否則編譯器將拋出編譯時錯誤。
實現(xiàn) Interface1 和 Interface2 的一個簡單類是:
package com.journaldev.java8.defaultmethod;
public class MyClass implements Interface1, Interface2 {
@Override
public void method2() {
}
@Override
public void method1(String str) {
}
@Override
public void log(String str){
System.out.println("MyClass logging::"+str);
Interface1.print("abc");
}
}
關(guān)于 java 接口默認(rèn)方法的要點:
Java 接口默認(rèn)方法將幫助我們擴(kuò)展接口,而不必?fù)?dān)心破壞實現(xiàn)類。
Java 接口默認(rèn)方法彌補了接口和抽象類之間的差異。
Java 8 接口默認(rèn)方法將幫助我們避免使用工具類,例如所有 Collections 類方法都可以在接口本身中提供。
Java 接口默認(rèn)方法將幫助我們刪除基本實現(xiàn)類,我們可以提供默認(rèn)實現(xiàn),實現(xiàn)類可以選擇重寫哪個。
在接口中引入默認(rèn)方法的一個主要原因是為了增強 Java 8 中的 Collections API 以支持 lambda 表達(dá)式。
如果層次結(jié)構(gòu)中的任何類具有具有相同簽名的方法,則默認(rèn)方法將變得不相關(guān)。默認(rèn)方法不能重寫 java.lang.Object 中的方法。推理非常簡單,因為 Object 是所有 java 類的基類。因此,即使我們在接口中將 Object 類方法定義為默認(rèn)方法,它也將是無用的,因為 Object 類方法將始終被使用。這就是為什么要避免混淆,我們不能有覆蓋 Object 類方法的默認(rèn)方法。
Java 接口默認(rèn)方法也稱為 Defender 方法或虛擬擴(kuò)展方法。
Java 接口靜態(tài)方法
Java 接口靜態(tài)方法與默認(rèn)方法類似,只是我們不能在實現(xiàn)類中重寫它們。這個特性有助于我們避免實現(xiàn)類中的糟糕實現(xiàn)帶來的不希望的結(jié)果。讓我們用一個簡單的例子來研究這個問題。
package com.journaldev.java8.staticmethod;
public interface MyData {
default void print(String str) {
if (!isNull(str))
System.out.println("MyData Print::" + str);
}
static boolean isNull(String str) {
System.out.println("Interface Null Check");
return str == null ? true : "".equals(str) ? true : false;
}
}
現(xiàn)在讓我們看看一個實現(xiàn)類,它的 isNull()方法的實現(xiàn)很差。
package com.journaldev.java8.staticmethod;
public class MyDataImpl implements MyData {
public boolean isNull(String str) {
System.out.println("Impl Null Check");
return str == null ? true : false;
}
public static void main(String args[]){
MyDataImpl obj = new MyDataImpl();
obj.print("");
obj.isNull("abc");
}
}
注意,isNull(String str)是一個簡單的類方法,它沒有重寫接口方法。例如,如果我們將@Override 注釋添加到 isNull()方法中,將導(dǎo)致編譯器錯誤。
現(xiàn)在,當(dāng)我們運行應(yīng)用程序時,會得到以下輸出。
Interface Null Check
Impl Null Check
如果我們將接口方法從 static 設(shè)置為 default,我們將得到以下輸出。
Impl Null Check
MyData Print::
Impl Null Check
Java 接口靜態(tài)方法僅對接口方法可見,如果我們從 MyDataImpl 類中移除 isNull()方法,我們將無法將其用于 MyDataImpl 對象。但是和其他靜態(tài)方法一樣,我們可以使用類名來使用接口靜態(tài)方法。例如,有效語句將是:
boolean result = MyData.isNull("abc");
java 接口靜態(tài)方法要點:
- Java 接口靜態(tài)方法是接口的一部分,不能用于實現(xiàn)類對象。
- Java 接口靜態(tài)方法適合于提供實用方法,例如空檢查、集合排序等。
- Java 接口靜態(tài)方法通過不允許實現(xiàn)類重寫它們來幫助我們提供安全性。
- 我們不能為 Object 類方法定義接口靜態(tài)方法,我們將得到編譯器錯誤為“這個靜態(tài)方法不能從 Object 中隱藏實例方法”。這是因為在 java 中不允許這樣做,因為 Object 是所有類的基類,我們不能有一個類級靜態(tài)方法和另一個具有相同簽名的實例方法。
- 我們可以使用 java 接口靜態(tài)方法來移除諸如集合之類的實用工具類,并將其所有靜態(tài)方法移動到相應(yīng)的接口,這樣就很容易找到和使用。
Java 函數(shù)式接口
在結(jié)束本文之前,我想簡單介紹一下功能接口。只有一個抽象方法的接口稱為函數(shù)式接口。
引入了一個新的注釋@functionainterface 來將接口標(biāo)記為 Functional 接口。@functionainterface 注釋是一種避免在功能接口中意外添加抽象方法的工具。這是可選的,但使用它是很好的實踐。
Java 8 的功能性接口是人們期待已久且備受關(guān)注的特性,因為它使我們能夠使用 lambda 表達(dá)式來實例化它們。添加了一個新的包 java.util.function 和一堆函數(shù)接口,為 lambda 表達(dá)式和方法引用提供目標(biāo)類型。我們將在以后的文章中研究函數(shù)接口和 lambda 表達(dá)式。