目錄:
- 接口的定義
- jdk7-9,接口屬性的變化
- jdk8,default、public static method的提出解決了什么問題,使用時需要注意什么
- jdk9的補充(引入private method、private static method)
- 新老生常談:接口和抽象類的對比
- 單繼承還是多繼承
一、接口的定義:
首先讓我們看一下接口的最新定義:What is an Interface,里面提到:
In the Java programming language, an interface is a reference type, similar to a class, that can contain only constants, method signatures, default methods, static methods, and nested types. Method bodies exist only for default methods and static methods. Interfaces cannot be instantiated—they can only be implemented by classes or extended by other interfaces.
再來看一下jdk1.7時的定義(具體原版定義官網已經找不到了,只能通過書籍及博客找到大概的版本):
An interface in Java is similar to a class, but the body of an interface can include only abstract methods and final fields (constants). A class implements an interface by providing code for each method declared by the interface.
我們知道,在Java中,接口不是類,而是對類的一組需求描述,類遵循接口描述的統一格式進行定義。Java不支持多繼承,主要是為了避免多繼承會讓語言本身變得復雜(像C++),效率也會降低。而接口可以提供多繼承的大多數好處,同時避免多重繼承的復雜性和低效性。
通過對比上面兩段定義,可以看出最新的接口多了一些新的屬性,接下來主要圍繞這些新屬性來理解接口的定義。
二、jdk7-jdk9,接口的變化:
然后再來簡單看一下java interface的一個演變過程:
-
Constants (until Java 1.7)
-
Method signatures (until Java 1.7)
-
Nested types (until Java 1.7)
-
Default methods (since 1.8)
-
Static methods (since 1.8)
-
Private methods (since 1.9)
-
Private static methods (since 1.9)
三、default和public static method:
接着我們先從一個8年前的問題開始:如果接口中有個方法的定義是可以確定的(實現該接口的類必須重復實現該方法),如何更優雅的設計這個接口?
文中提到了許多折中的方法:通過接口里的靜態類、借助工具類的靜態方法等。
但到了jdk8開始,這個問題被解決了:接口引入了default關鍵字和靜態方法(public static method),接口中通過default修飾的方法可以有方法體,實現具體功能。看下面代碼:
public interface Skill{ void oldSkill(); default public void newSkill(){ System.out.print("實現了這個接口的類可以不實現這個方法"); } public static newStaticSkiil(){ System.out.print("實現了這個接口的類可以不實現這個靜態方法"); } }
引入default的目的官網有解釋:Default Method ,總的來說,引入default關鍵字是為了當接口有新的行為且現有依賴該接口的類與接口不需做任何改變,可以不再對於已實現該接口的類或繼承該接口的接口進行修改,可以更加方便的拓展現有接口。
文中還指出,對於拓展了包含default method的接口或類:
- Not mention the default method at all, which lets your extended interface inherit the default method.(可以不理,直接繼承)
- Redeclare the default method, which makes it
abstract
.(重新聲明為抽象方法) - Redefine the default method, which overrides it.(重新定義方法)
最后,還提到了jdk8開始,接口可以支持靜態方法(public static method),這些都是為了接口可以有更好的演變性。
例如,jdk8開始引入的Lambda語法以及Stream API,都是在接口層實現的,因此default及public static method的提出算是為新的設計服務,也響應了這么久以來定義接口方法的呼聲。
Collection接口中,default關鍵字的身影:
引入default關鍵字后,有些地方可能要注意一下。首先是如果類實現的多個接口都包含了對同一個方法的default定義,那這個方法必須重寫,看下面例子:
public interface Ina { default void say(){ System.out.println("say a"); } }
public interface Inb { default void say(){ System.out.println("say b"); } }
如果classc在實現這兩個接口時不重寫say方法,那編譯器無法確定到classc調用say方法時,選擇哪一個接口的方法。因此Classc必須重寫say:
public class classc implements Ina,Inb { @Override public void say(){ System.out.println("say c"); } }
當然,根據上面提示的3條規則,我們也可以把say方法重新聲明為抽象方法:
public interface Ind extends Ina{ public abstract void say(); }
四、jdk9的補充:
jdk9中,接口引入的私有方法(private method)和私有靜態方法(private static method)。由於不可被繼承,因此私有方法必須定義方法體才有意義。同時也意味着接口的私有方法和私有靜態方法不可被實現該接口的類或繼承該接口的接口調用或重寫。私有方法(private method)和私有靜態方法(private static method)的提出都是對jdk8提出的default和public static method的補充。
默認方法和靜態方法可以共享接口中的私有方法,因此避免了代碼冗余,這也使代碼更加清晰。如果私有方法是靜態的,那這個方法就屬於這個接口的。並且沒有靜態的私有方法只能被在接口中的實例調用。
五、接口和抽象類的區別:
抽象類定義:An abstract class is a class that is declared abstract—it may or may not include abstract methods. Abstract classes cannot be instantiated, but they can be subclassed.
jdk8之后,接口的行為不再局限在“描述”,接口的行為具有了“定義”,對后期接口的演變和拓展提供了便利性,也表示類的行為可以多繼承了;當然即使是多了這些新特性,接口還是和抽象類有一條最明顯的分界線:接口無狀態,抽象類只能單繼承。接口的成員域依然只能是常量,接口依然不能實例化。除此之外,還有權限上的區別,不支持protect聲明及包可見等屬性。
六、單繼承還是多繼承:
不過,接口的這些新屬性是為了更好的服務“單繼承多實現”的理念還是更好的往“多繼承”靠攏呢?網上有很多不同的聲音,而本人更傾向於更好的服務“單繼承多實現”,我們知道,最大的改變還是jdk8提出的default和public static method,接口跳出了只能“聲明”方法,只有public abstract method的界限,從而使得原有框架上多出了更多優雅的設計。打破的規則如果可以讓Java更優雅,那這規則就是應該被打破的。
參考:
《Java核心技術 卷1》
https://docs.oracle.com/javase/tutorial/java/IandI/createinterface.html
https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html
https://en.wikipedia.org/wiki/Interface_(Java)