abstract和interface關鍵字在Java中隨處可見,它是Java三大特性封裝、繼承、多態特性的實現重要支柱之一。interface關鍵字用於定義接口抽象,其本質上是用於定義類型、定義類所具有的能力。但是新手往往錯誤的使用了abstract和interface,小捌其實也一樣犯錯誤,這篇文章我們盤一盤interface接口和abstract抽象類的使用。
文章開始前建議帶著兩個疑問閱讀:
abstract和interface有什麼區別?
abstract和interface應該怎麼選?
2、準則定義接口的時候,有一些準則可以參考,根據這些準則可以更好的確定自己應不應該定義接口、或者是否有其他更好的代替方案。(注意小捌說的點不是絕對正確的,實際開發過程中要具體分析,有不對的可以互相交流。)
2.1 接口優先於抽象類小捌這裡用JDK的源碼HashMap的繼承體系來說明接口優先於抽象類這一點。
HashMap繼承體系類圖結構:
HashMap的頂層接口:
public interface Map<K,V>{}
複製代碼
HashMap實現的抽象類:
public abstract class AbstractMap<K,V> implements Map<K,V> {}
複製代碼
可以看到HashMap繼承了AbstractMap抽象類實現了Map接口,但為什麼說接口優先於抽象類呢?這些因為Java是單繼承多實現,HashMap繼承了AbstractMap抽象類之後就無法繼承其他類了,如果是接口就沒有這個限制,比如HashMap還需要提供序列化和克隆的功能,HashMap就可以實現三個接口Map<K,V>, Cloneable, Serializable。
既然這樣為什麼HashMap還要去繼承AbstractMap抽象類呢?
這是因為在JDK源碼設計中,Map結構JDK需要提供部分方法的默認實現,因此JDK的作者們單獨拉取了一個抽象類來實現這些方法;儘管Java8 Oracle嘗試在接口中提供靜態方法和普通方法,但是小捌認為沒有到一定的需求程度,儘量、甚至完全不應該將方法實現定義在接口中。
abstract和interface有什麼區別呢?
其實在Java8之後區別在不斷的縮小,但是總體上來說還是兩個完全不同的概念:
抽象類abstract的特點:
接口interface的特點:
接口中的方法,都被public來修飾
接口中沒有構造方法,不能實例化接口對象
比較項抽象類(abstract)接口(interface)多繼承不支持(只能繼承一個抽象類)支持(類可以實現很多個接口)方法抽象類則可以同時包含抽象和非抽象的方法接口中所有的方法隱含都是抽象的(Java可以定義靜態方法)構造器允許不允許實例化不能實例化不能實例化訪問修飾符抽象類可以使用public、default;抽象方法可以使用public、default、protected;普通方法可以使用public、default、protected、private接口可以使用public、default;方法默認public;總結:
在整個抽象實現體系中,必須提供一些方法的默認實現,可以使用抽象類(因為非常不建議在接口中直接實現某些方法)
如果不需要提供默認實現,且需要實現多繼承的功能就使用接口
2.2 接口中不應該實現方法接口無處不在,接口作為類體系結構的最頂層,接口提供的一切約束和規範都是直接影響下層實現類。因此不建議在接口中實現具體的方法,儘管Java8之後的接口定義可以提供靜態方法實現和普通方法實現,但是這種實現方式有很大的風險,除非你的接口設計真的很完美,完美到能對所有的實現類都負責任的說你的邏輯永遠不會變。要不然接口的具體實現方法邏輯修改後,下面那些使用了該方法的類都得遭殃。
因此接口儘可能的只用來定義類型、定義類所具有的能力。如果一定要定義實現,可以考慮使用抽象類來定義。
2.3 接口不應該用於導出常量由於接口中定義常量非常方便,因此有一些小夥伴會使用接口直接作為常量導出類,比如如下這種方式:
/**
* <p>
* 緩存key
* </p>
*
* @Author: Liziba
* @Date: 2021/11/2 23:12
*/
public interface CacheKey {
String USER = "user";
String ORDER = "order";
String MAIL = "mail";
}
複製代碼
它雖然看起來非常簡便、使用上也沒什麼問題。但是問題就出在接口它不是用來給你導出常量的,如果需要定義常量我們可以使用枚舉或者常量類,比如如下這種方法:
public class CacheKey {
public static final String USER = "user";
public static final String ORDER = "order";
public static final String MAIL = "mail";
}
複製代碼
注意小捌這裡說的是不要拿接口僅僅只作為常量導出類,而不是說不能在接口中定義常量,如果部分常量是類抽象類型中統一使用的可以考慮這樣設計(但是也不推薦啦!),單獨抽出常量類來管理這些常量往往要更好一些的。
作者:李子捌
連結:https://juejin.cn/post/7026136826490388516
來源:稀土掘金
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。