# 抽象類別與抽象方法 & 介面(interface) ## 前瞻知識: #### 方法 method: ```python class Power :// 此稱為一個類別 def computer (): // 此稱為一個方法 ... def phone () // 這也叫一個方法 ... ``` #### 功能 function: ```python def computer: // 此稱為一個功能 function ... def phone: // 功能function ... ``` 所以如果當一個function(功能) 被 class 包起來的時候(被涵蓋在裡面),此時我們就將function(功能)稱為 method 方法。 # 抽象方法 & 抽象類別 (abstraction method & abstraction class): 先說教科書上的定義: ### 抽象方法(abstraction method): > 只有定義方法(成員函數)的「呼叫方式」,而沒有定義方法實際上到底要做什麼事情的程式碼。也就是說,抽象方法只有方法的頭(名稱和小括號()區塊),而沒有方法的身體(沒有大括號{ }區塊)。 ```java abstract class Animal { // 抽象類別 class bird (); // 這邊沒有身體(也就是沒有方法 method 的實作) } ``` 特別注意的是,抽象方法只能定義在抽象類別的{ }之中,否則會編譯錯誤。 ```java abstract class Animal { class bird () { ... } // <- error } ``` Abstract method 則是一種只有宣告,而沒有實作內容的一種特殊 method。Abstract method 主要的目標是預先宣告 method,但是把實作的工作交給繼承的類別來做。透過使用 abstract method,就可以讓衍生類別具有共同行為,但是實際的做法則由各個類別自訂。 抽象類別(abstraction class): 類別裡面包含了「至少一個」抽象方法,就稱為抽象類別。如果一個類別中包含了至少一個抽象方法,則該類別就必須被宣告成「抽象類別」(加上abstract關鍵字)例如: 「public abstract class Animal{ … … … }」。 這樣的定義讀者應該還是看不太懂,以實例來說明。如果有一個表格,上面寫著「姓名:______」,你就明確知道這裡要填上你的名字。「姓名:______」就類似於「抽象方法」,因為它只定義了呼叫方式,但沒有實際可作用的程式碼。 而「抽象類別」就是指裡面含有「至少一個」抽象方法的類別,例如機場海關的入境表格就是一個抽象類別,表格的格式雖然都有清楚的標示和規範,但實際的內容全部都是空白的,要旅客自己填寫。 入境表格「強迫」每個旅客一定要複寫(override)掉抽象方法,也就是要把入境表格的抽象方法給填上每個旅客自己的資料,表格才會發揮效用。而旅客填表的動作,就稱為「實作」(implement),也就是字面上的意思:把實際的內容(程式碼)給實際做出來。 為什麼要這麼麻煩?舉這個入境表格的例子,大家應該就立刻明白了。由於每個旅客的個人資料都是不同的,入境表格只能預先做好格式,但沒辦法幫大家填表,必須要透過每個物件自行以複寫來實作,這張表格才能發揮效用。 抽象類別或抽象方法在大型、多人協作的工作專案中比較有用,小型程式直接實作就好了。抽象類別或抽象方法可以幫助我們規劃程式的架構,讓多人合作的大型軟體專案或軟體工程變得更有架構和規劃,更有系統性,是屬於「架構設計」的功能。抽象類別或抽象方法可以方便上層的領導人員規劃程式架構,指派工作給各個軟體工程師去實作出來,並且可以更明確的劃分責任範圍,讓除錯與維護更有效率(例如主管罵說:這個方法(method)的實作亂七八糟,是誰寫的code?給我重寫)。 就好像公司創立初期業務量少,老闆一人就可以實作公司所有的大小事務。不過當公司發展到一定的規模時,老闆就必須制定良好的公司組織結構和章程規範、制定工作目標(抽象),並雇用員工來實踐所下達的任務(實作),而每個員工各司其職,做不一樣的事情,最後整合起來完成目標。 由於抽象類別包含了至少一個抽象方法,抽象方法沒有實作的程式碼,因此無法直接被實體化,所以抽象類別(abstract class)必須先以「繼承」的方式轉為一般的類別(class),然後在(一般的)類別中把抽象方法給實作出來,之後再實體化產生物件,讓物件使用(具體的)方法。 宣告抽象類別,實際的程式碼如下: abstract class Animal { // 以「abstract」關鍵字,定義一個抽象類別Animal String name ; // 定義字串變數name abstract void cry(); // 以「abstract」修飾子,定義抽象方法cry() } class Cat extends Animal { //(一般)類別Cat繼承自抽象類別Animal void cry(){ // 在一般類別中以複寫實作抽象方法(此時不用加 @override) System.out.println(”喵喵叫”); // 方法的實作程式碼 } } class Dog extends Animal { void cry(){ System.out.println(”汪汪叫”); // 每個類別都能把抽象方法複寫、實作成不同的功能 } } class Chicken extends Animal { void cry(){ System.out.println(”咕咕咕 ”); // 每個類別都能把抽象方法複寫、實作成不同的功能 } } 其餘依此類推。 ----------------------------------------------------------------------------------------- 介面(interface): 「介面」(interface)是JAVA很有名的功能,其實它和抽象類別(abstract class)很像。我們知道類別(class)是張設計圖,經過new運算子和建構子(constructor)做了實體化之後可以產生具體的物件,物件便可執行所擁有的方法(method)和其他物件進行互動。 介面是定義不同類別之間的一致行為,也就是一些共同的屬性(常數)或抽象方法。 就像不同作業系統的電腦,經由TCP / IP介面,一樣可以建立連線。 我們在類別(class)裡面會清楚的把所有屬性(成員變數)、以及方法(成員函數)的定義明確寫出來,所以一旦實體化之後,物件就能直接使用這個方法。 而介面(interface)的意思就是,我們在類別中的方法(成員函數)「全部」都寫成「抽象方法」,以及其屬性(成員變數)「全部」都設為「常數」。 剛才入境表格的例子就是一個介面(interface)的典型範例,由於每個旅客的「姓名」、「護照號碼」、「出生年月日」…等基本資料都不同,我們無法在定義類別(class)的時候就把它們填上,於是就只能定義一個「空表格介面」,並且在之後讓每個旅客自己去實作它們各自的方法。或者換個角度來說同一件事:我們把所有旅客所共有的抽象方法(例如姓名、護照號碼、出生年月日…)給「抽取」出來做成介面,讓每個旅客自己去實作各自的抽象方法。 實際撰寫介面(interface)的程式碼,和撰寫類別差不多,如下: interface EmptyForm { // 定義一個名為空表格(Empty form)的介面 public static final int a = 5 ; // 介面中只能定義常數(此處用final修飾子) public abstract void write(); // 介面中的抽象方法,前面要加上「abstract」修飾子 } 不過這邊有個小技巧,由於介面裡面的屬性和方法一定「全部」都是「常數」和「抽象方法」,也一定要是public的(不然怎麼讓外界實作?),因此所有的修飾子都可以省略不寫(寫了也無妨),如下: interface EmptyForm { int a = 5 ; // 所有的修飾子都可以省略不寫 void write(); // 所有的修飾子都可以省略不寫 } 以上注意,介面名稱的首個英文字母,習慣上像類別名稱一樣會使用大寫,便於區別。 ----------------------------------------------------------------------------------------- 介面的實作: 那,要怎麼實作介面呢?也就是如何來「填表」呢?由於介面全部都是抽象方法,因此無法直接實體化產生物件。所以,要先把介面轉換為一般類別(class),再透過(一般)類別複寫掉介面的抽象方法,此時會使用到implements(實作)關鍵字: class form implements EmptyForm { // 定義一個名為form的類別,來實作EmptyForm介面 public void write(){ // 在form類別中實作介面EmptyForm的抽象方法write() System.out.println(”護照號碼123456789”); // write()的實作程式碼 } } 介面經過類別的implements之後,我們就可以像平常一樣,從類別(class)去實體化產生物件。 JAVA還提供了一個很方便的功能,就是一個(一般的)類別可以同時實作多個介面,也就是一個類別可以同時把多個不同介面中的抽象方法彙整起來,並一次實作出來,再實體化產生物件。 註:這和多重繼承是不一樣的概念喔。 實際的程式碼如下: class form implements EmptyFormA , EmptyFormB , EmptyFormC { // 介面的名稱用逗號做區隔 … … … … … … … … … // 在form類別中實作三個介面的抽象方法 } 嗯,你說已經忘記之前寫過的介面,還要回頭查看介面中有那些抽象方法還沒有被實作?沒問題,這些IDE(整合開發環境Integrated Development Environment,例如Eclipse、IntelliJ IDEA、NetBeans …)都已經幫你想到了,IDE會幫你記住介面中還有哪些抽象方法還沒有被實作,然後適時提醒你,並且可以開啟子視窗查看目前有哪些抽象方法已經有實作、而哪些還沒被實作,甚至還可以讓IDE幫你把尚未被實作的抽象方法給一次全部列出來讓你「填空」,真是非常方便。 ----------------------------------------------------------------------------------------- 介面的繼承: 介面就和類別一樣,也可以使用繼承功能,產生父介面與子介面。介面繼承的語法和類別一樣,也是用extends關鍵字: interface A { // 定義一個(父)介面A int e ; void f(); } interface B extends A { // 定義一個子介面B int g; void h(); } 不過,由於介面裡面的內容比較單純,只是一個空殼,所以做「介面的多重繼承」比較不會像類別的多重繼承那樣複雜、混亂,所以可以放心使用。介面的多重繼承的程式碼為: interface X extends A , B , C { // 介面的名稱用逗號做區隔 … … … } 這樣一來,介面X就會同時擁有介面A、B、C三者的常數屬性和抽象方法了。 還有一種比較進階的複合語法,就是當一個(一般)類別需要同時繼承自某個類別,並且同時實作介面,語法寫作: class C extends A implements B { // 必須把extends寫在implements前面 … … … } 其中C是子類別,A是父類別,而B是介面名稱。不過,建議還不太熟練的讀者不一定要這麼寫,老實的分開寫也沒關係。 JAVA如何用介面(間接的)實現多重繼承? 一個類別可以同時實作多個介面,代表可以用介面的方式來間接的實現多重繼承。 public class Circle extends Shape implements ShapeInterface , ShapeInfo { … … … public void area(){ // area(){ 方法的實作程式碼 }; public void perimeter(){ // perimeter(){ 方法的實作程式碼 }; public void show(){ // show(){ 方法的實作程式碼 }; } 上述Circle類別繼承Shape抽象類別,並且同時實作兩個介面(ShapeInterface、ShapeInfo)的三個抽象方法,就是間接的透過介面,做到多重繼承。 ----------------------------------------------------------------------------------------- 小整理: (一般)類別(class): 包含成員變數和一般的(具體)方法。 抽象類別(abstract class): 包含至少一個抽象方法,不過也可以包含一般的(具體)方法。 抽象類別的實作: 靠繼承到一般類別(關鍵字extends)。 介面(interface): 介面的「全部」成員都是「常數」和「抽象方法」。 介面的實作: 靠一般類別的實作(關鍵字implements)。
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.