# 虎年行大運 ~ Java Foundamental 系列 - 繼承、抽象與介面 ## 【前言】 - 繼承 (Inherit) - 抽象 (Abstract) - 介面 (Interface) 三個在 Java 中非常常見的名詞,到底你真的懂他們存在的用意與用途嗎? ## 什麼是繼承? 簡單來說,它是一個承上啟下的動作,使得繼承的物件可以擁有被繼承物件的方法與特性。 今天有兩個實體,都有一個相同的方法,把這些相同的方法抽出來,獨立寫成一個父物件,接著讓兩個實體都繼承這個父物件,這樣兩個子物件就不需要那些相同的方法,而可以單純擁有各自不同的方法。 **繼承的意義,在於透過父類的實作去減少多個子類中重複的程式碼,讓多個子類可以保持獨立性。** 繼承的使用方式為:單一類別僅能繼承一個父物件。但反過來一個父物件可以被多個子物件繼承。 ```java= class test {} class test2 {} class test3 extends test {} // 正確 class test4 extends test {} // 正確 class test5 extends test, test2 {} // Compile Error ``` ## 什麼是介面? 簡單來說,它是一個與外界聯繫的接口,通過這個接口,外界不需要關心具體內部的做法或流程為何,只需要關心產出的結果,因此達到解耦的效果。 **介面的意義,在於分離宣告與實作,讓外部引用能夠預期結果,而不需要關心內部流程。** 與繼承的使用方式不同,介面類在建構上不能夠進行任何方法的實作,只能宣告 (雖然在 Java 8 後已更新成可以寫實作了,但這邊建議去保持介面的乾凈,畢竟功能跟原則還是需要取捨) 在使用上則是透過 **implements** 的方式讓其他類進行這些宣告方法的實作,且並不限制單一類別去實作多個介面類。 介面的繼承非常特別,打破一般只能繼承一個物件的特性,介面若是繼承介面的話,可以繼承多個介面。 但介面並不允許實作介面。 ```java= interface test {} interface test2 {} interface test3 extends test, test2{} // 正確 interface test4 implements test, test2 {} // Compile Error ``` ## 什麼是抽象? 抽象是一個基本依賴繼承功能完成介面特性的類別,在繼承的架構中得以達到接口與實作分開的處理方式。但由於其本身還是一個類,所以同時也支持方法的實作,剩下僅有宣告的方法則仰賴於繼承物件的實作。 如同介面一樣,需要依賴於繼承(實作)物件的實體而無法直接被呼叫使用 但在抽象類中的實作"**可以使用那些未被實作的方法**",因此我們可以在抽象類中,先行設計出"**流程**",借此達成宣告、流程與實作三者的分離。 簡單來說,抽象是一個擁有介面特性的一般類,這樣會比較好理解。 **抽象的意義,在於繼承的架構中協助分離重複的程式碼,並且同時分離方法的宣告與實作** ## 選擇使用抽象還是介面的時機點 由於這兩個功能類的特性實在是很相似,以至於在使用上常會混亂。因此有必要從用途當作理解開始的出發點。兩者在用途上有著比較顯著的差異,從這個差異去作深入探討會比較容易理解兩者的不同。 簡單的說,兩個類別若具備了明確的、可定義的、可關聯的關係,且有一些獨立需要設計的方法時,"善用" 抽象類去建構會是較好的做法。 而介面則是更適合非有明確關聯,但可能動作、流程、行為相似的類,又或者僅是需要替一群功能類設立一個接口類。 以下舉個簡單的例子: 賓士是世界上高級油車品牌之一;特斯拉是世界上電動車品牌之一;古早的馬拉車也算是最原始的車了,三者都是車,他們在車本身用於幫助省力移動的用途是一樣的,但在結構、能源驅動的來源卻截然不同。下面 Kai 將帶著你一步步了解何處使用抽象、何處使用介面。 - 首先我們設立一個 Car 的父類別,並讓三個子類別(賓士、特斯拉、馬車) 作繼承 ![](https://i.imgur.com/uLVByrU.png) --- - 明確定義其類別為 Abstract 和一般 Class,這樣未來可以透過 Abstract 的特性達到宣告與實作分離 ![](https://i.imgur.com/qQ3UyIt.png) --- - 定義 Abstract 屬性與方法,以及未實作的方法宣告;定義一般 Class 應實作的部分。 我們定義了能源類型 Energy 屬性、增加能源的方法 addEnergy(),驅動能源方法 burnEnergy()、啟動狀態判定 startReady()、以及最重要的行駛 move() - 而在啟動的過程一定會需要使用到能源,因此我們直接把 burnEnergy() 寫入 startReady() 方法中,後續當然還需要其他的模組支援,但這也可以顯現了使用者接下來只需要直接使用 startReady() 即可,不用再關注 burnEnergy() 等事情。雖然 burnEnergy() 的實作尚未完成,但已經可以預期到整個流程就是被規劃好的,這也是抽象用來分離宣告、流程與實作的方式 - 可以發現某些方法在 Horse 的類別中非常彆扭,這涉及到了 S.O.L.I.D 的設計原則,畢竟馬車不是機械車,不需要實作驅動能源的方法 ![](https://i.imgur.com/qKmBOsA.png) --- - 因此為了符合 S.O.L.I.D 的設計原則,我們增設了中間層的類別去做區分機械和動物出力的車類型,這些新設立的類也會是 Abstract 類,而後我們終於能夠把機械車和動物出力的車類型給分別開來。畢竟世界上除了馬車以外,還有驢車、牛車、甚至大象都可以是代步工具。 ![](https://i.imgur.com/vRn3AY3.png) --- - 最後我們終於要把介面給加進來,世界上不是只有車可以當作代步工具,舉凡飛機、船等都可以是代步工具。因此 move() 這個方法被侷限在 Car 類別中是件很尷尬的事情,當今天使用者不想要透過車做移動的時候,變成要引用其他類不能透過相同的類宣告,這樣就失去了擴增的彈性,因此我們再將 move() 抽出來放入 Mobile 的介面中,讓飛機與船都可以實作 Mobile 介面,這樣就可以透過 Mobile 真正去決定不同的代步工具了。 ![](https://i.imgur.com/dmOhwOa.png) > 原諒 Kai 的結構和取名很爛,因為這部分算是隨手想的例子... ## 【結語】 講解的部分也許不是很精簡,未來在更多的設計經驗下,持續的做這部分的更新,希望能以更好的方式讓大家更快理解這幾個名詞的意義和用途。 首頁 [Kai 個人技術 Hackmd](/2G-RoB0QTrKzkftH2uLueA) ###### tags: `Java Foundament`