類別繼承與成員覆寫

繼承類 Extend a class

透過 extends 關鍵字,可以從父類(superclass / 超類)衍生出子類(subclass)

使用 super 關鍵字,就能在子類內呼叫父類的成員(方法、建構子等等)

class Television {
  void turnOn() {
     ...
     //父類方法內執行的程式碼
  }
}

// SmartTelevision 是子類,Television 是父類
class SmartTelevision extends Television {
  @override
  void turnOn() {
    super.turnOn(); // 呼叫父類的 turnOn 方法
    //子類專屬程式碼
    ...
  }
}Code language: Dart (dart)

舉例來說,一般電視做為父類,智慧電視就是子類,智慧電視繼承了一般電視的功能。

extends 還有另一種使用情境:泛型章節中與參數化型別相關的內容。

實際範例
class Television {
  void turnOn() {
    print("電視通電,輸出基礎畫面");
  }
}

// SmartTelevision 是子類,Television 是父類
class SmartTelevision extends Television {
  @override
  void turnOn() {
    super.turnOn(); // 呼叫父類 turnOn 方法
    //子類專屬程式碼
    print("智慧電視:啟動網路模組、載入智慧系統首頁");
  }
}

void main() {
  // 一般電視實體
  Television normalTv = Television();
  print("===== 一般電視開機 =====");
  normalTv.turnOn();

  // 智慧電視實體
  SmartTelevision smartTv = SmartTelevision();
  print("\n===== 智慧電視開機 =====");
  smartTv.turnOn();
}Code language: Dart (dart)

執行結果

D:\dartdemo\firstdart>dart run
Building package executable... (1.4s)
Built firstdart:firstdart.
===== 一般電視開機 =====
電視通電,輸出基礎畫面

===== 智慧電視開機 =====
電視通電,輸出基礎畫面

覆寫成員 Overriding members

1. 可覆寫的項目

子類能夠覆寫:實體方法(包含運算子)、getter、setter

2. @override 註解

用途:明確宣告此方法主動覆寫父類成員,由編譯器協助檢查語法規則。

class Television {
  set contrast(int value) {}
}

class SmartTelevision extends Television {
  @override //告訴編譯器,這個方法是子類覆寫父類
  set contrast(num value) {}
}
Code language: JavaScript (javascript)

3. 覆寫必須遵守的規則

  1. 回傳型別:覆寫方法的回傳型別必須和父類方法一致,或是父類回傳型別的子型別
  2. 參數型別:覆寫方法參數型別必須和父類參數型別相同,或是父類參數型別的父型別(範例 intnum,num 為 int 的父型別,屬合法寫法)。
  3. 位置參數數量:若被覆寫的方法有 n 個位置參數,覆寫的方法也必須具備 n 個位置參數。
  4. 泛型約束:泛型方法不能覆寫非泛型方法;非泛型方法也無法覆寫泛型方法。

4. covariant 關鍵字(型別縮限特殊情境)

標準規則不允許縮小參數型別(向下轉型可能在執行階段觸發型別錯誤);

如果開發者能夠保證不會產生型別錯誤,可在參數加上 covariant,實現參數型別縮限。

當你覆寫運算子 == 時,一定要同時覆寫 ObjecthashCode getter


noSuchMethod ()

1. 用途

當程式嘗試呼叫不存在的實體方法 / 實體變數時,可以覆寫 noSuchMethod() 進行攔截,自訂處理邏輯。

若沒有覆寫,存取不存在的成員時會直接拋出 NoSuchMethodError

class A {
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ${invocation.memberName}');
  }
}
Code language: JavaScript (javascript)
  • Invocation 物件:儲存本次呼叫資訊(成員名稱、參數等等)
2. 能夠觸發尚未實作方法呼叫的兩個前提(滿足其一即可)
  1. 呼叫接收者的靜態型別為 dynamic
  2. 接收者靜態型別宣告了該未實作方法(可以是抽象方法),並且接收者的動態型別擁有和 Object 內建版本相異的 noSuchMethod() 實作。

範例

class A {
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ${invocation.memberName}');
    // 額外印出呼叫參數、呼叫類型,展示完整呼叫資訊
    print('呼叫參數:${invocation.arguments}');
    print('呼叫類型:${invocation.runtimeType}');
  }
}

void main() {
  A obj = A();
  // 呼叫不存在方法,觸發 noSuchMethod
  obj.hello("test", 123);
  // 存取不存在屬性
  obj.unknownValue;
}Code language: Dart (dart)
D:\dartdemo\firstdart>dart run
Building package executable...
Failed to build firstdart:firstdart:
bin/firstdart.dart:6:30: Error: The getter 'arguments' isn't defined for the type 'Invocation'.
 - 'Invocation' is from 'dart:core'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'arguments'.
    print('cal:${invocation.arguments}');
                             ^^^^^^^^^
bin/firstdart.dart:14:7: Error: The method 'hello' isn't defined for the type 'A'.
 - 'A' is from 'bin/firstdart.dart'.
Try correcting the name to the name of an existing method, or defining a method named 'hello'.
  obj.hello("test", 123);
      ^^^^^
bin/firstdart.dart:16:7: Error: The getter 'unknownValue' isn't defined for the type 'A'.
 - 'A' is from 'bin/firstdart.dart'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'unknownValue'.
  obj.unknownValue;Code language: PHP (php)

類別繼承與成員覆寫

Next:

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *