Java – 物件導向 – 抽象類別

先撇開程式的概念, 回到現實生活的認知. 手機(mobile) 應該是眾所皆知的名詞, 就從手機開始吧.

手機, 至少具有撥號打電話的功能, 開關機等等, 所以就開始先設計了很陽春的手機類別:

class Mobile {
  protected String phoneNumber;	// 電話號碼
  protected boolean isOn;		// 開關機狀態
  // 以電話號碼來建構出物件實體
  Mobile(String phoneNumber){
    this.phoneNumber = phoneNumber;
  }
  // 切換開關
  void switchPower() {
    isOn = !isOn;
  }
  // 傳回目前開關機狀態
  boolean isPowerOnOrOff() {
    return isOn;
  }
  // 傳回手機號碼
  String getPhoneNumber() {
    return phoneNumber;
  }
  // 撥號
  void callOut(String callNumber) {
    
  }
}

對於撥號這件事, 也就是 callOut() 方法該如何實現? 各家手機業者的實際處理手法可能各有不同, 因此, 根本不需再在此進行定義, 而也因為沒有任何定義, 就不需要撰寫 { …… }, 就必須宣告為一個 abstract method 抽象方法, 而擁有抽象方法的類別, 則必須宣告為抽象類別(abstract class).

// 手機類別
abstract class Mobile {
  protected String phoneNumber;	// 電話號碼
  protected boolean isOn;		// 開關機狀態
  // 以電話號碼來建構出物件實體
  Mobile(String phoneNumber){
    this.phoneNumber = phoneNumber;
  }
  // 切換開關
  void switchPower() {
    isOn = !isOn;
  }
  // 傳回目前開關機狀態
  boolean isPowerOnOrOff() {
    return isOn;
  }
  // 傳回手機號碼
  String getPhoneNumber() {
    return phoneNumber;
  }
  // 撥號
  abstract void callOut(String callNumber);
}

因為抽象方法只有宣告, 而沒有定義實作, 表示了一個不清不楚的方法, 只知道該類別可以callOut(撥號), 如何處理, 卻沒有定義. 一個具有抽象的方法, 也就是一個不清不楚的類別, 因此就無法直接建構出物件實體. 好, 可以白話文來說, 不清不楚就是抽象的意思.

那就交給繼承的子類別來實作囉, 一個iPhone, 另一個是Android:

class iPhone extends Mobile {
  iPhone(String phoneNumber){
    super(phoneNumber);
  }
  // 撥號
  void callOut(String callNumber) {
    System.out.println("iPhone:" + callNumber);
  }
}
class Android extends Mobile {
  Android(String phoneNumber){
    super(phoneNumber);
  }
  // 撥號
  void callOut(String callNumber) {
    System.out.println("Android:" + callNumber);
  }
}

對於 iPhone 與 Android 而言, 都是 手機 Mobile (is-a Mobile). 都具有 Mobile 的屬性及方法, 唯獨在呼叫使用 callOut() 的時候, 執行的內容不同而已.

以下範例就上述兩個類別的手機物件實體來玩.

public class TestMobile {
  public static void main(String[] args) {
    Android phone1 = new Android("111");
    iPhone phone2 = new iPhone("222");
    call(phone1, "3434");
    call(phone2, "3434");
  }
  // 設計一個打電話的方法, 傳遞一個手機, 以及電話號碼
  static void call(Mobile mobile, String num) {
    mobile.callOut(num);		// 因為只要是 Mobile 就會有 callOut(String)方法
  }
}

執行結果:

Android:3434
iPhone:3434

 

或是修改如下:

public class TestMobile {
  public static void main(String[] args) {
    Mobile phone1 = new Android("111");
    Mobile phone2 = new iPhone("222");
    phone1.switchPower();
    System.out.println(phone1.getPhoneNumber());
    phone2.switchPower();
    System.out.println(phone2.getPhoneNumber());
    call(phone1, "3434");
    call(phone2, "3434");
  }
  // 設計一個打電話的方法, 傳遞一個手機, 以及電話號碼
  static void call(Mobile mobile, String num) {
    mobile.callOut(num);		// 因為只要是 Mobile 就會有 callOut(String)方法
  }
}

宣告為 Mobile, 而以 iPhone 或是 Android 來建構出物件實體. 因為

  • iPhone 繼承  Mobile, 所以 iPhone is-a Mobile
  • Android 繼承 Mobile, 所以 Android is-a Mobile

既然都是Mobile, 我要一支手機, 是以 iPhone 來產生一支手機的物件實體; 我要一支手機, 也可以用 Android 來產生物件實體. 對我而言, 這兩隻都是手機物件實體 (Mobile).

而都具有撥號功能, 所以我可以拿來撥號, 只是, 當撥號之後, 其內部實作的方式會不一樣.

 

好了, 回到程式面來探討其觀念及格式.

抽象類別(abstract cllass)還是類別, 只是:

  • 無法直接建構出物件實體
  • 可能擁有抽象方法, 也可能沒有.
  • 如果擁有抽象方法, 該類別一定是抽象類別

那, 抽象方法又是什麼? 就是只有定義, 而沒有任何實作的方法, 就必須宣告為抽象方法. 簡單的說, 就是完全沒有大括號({……})的部分, 直接以分號(;) 做結尾. 例如之前學過的方法:

void m1(){
// 程式區塊
}

而抽象方法則為:

abstract void m1();

其他的觀念都與一般的類別完全一樣.

 

 

%d bloggers like this: