iT邦幫忙

2024 iThome 鐵人賽

DAY 6
0
Mobile Development

從零開始以Flutter打造跨平台聊天APP系列 第 6

Day-6 Dart 簡介(5):Extends, Mixin, Implement, Interface

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20240907/20129540DAttnwLhc5.jpg

Dart 是物件導向的語言,擁有很多物件導向語言的特性。物件導向在「繼承」的問題上會衍生出許多問題,比如在 C++ 中允許一個 class 繼承多個 class,然而這會造成「菱形繼承問題」;在 Java 中僅允許「單一繼承」,一個類只能繼承一個類別。Java 通過 interface 來實現多重繼承的效果。interface 允許類別實作多個行為,但因為 interface 內不會進行實作,所以當 Java 中的 class 實作多個 interface 時,並不會產生「菱形繼承問題」。至於 Dart,與 Java 類似,class 只能一次繼承(extends)一個 class,但是,可以混入(with)多個 mixin。在 Dart 中,mixin 內是可以擁有具體的方法。為了避免 C++「菱形繼承問題」,mixin 無法使用繼承。

範例程式碼:https://github.com/ksw2000/ironman-2024

Extends

官方教學文件: https://dart.dev.org.tw/language/extend

Dart 在處理繼承的部份,大致與 Java 雷同,一樣使用 extends 關鍵字,並且使用 super 操作父母類別。如果要覆寫(override) 父母方法,可以加入 @override 裝飾器。

class Animal {
  String name;
  int age;

  Animal(this.name, this.age);

  void makeSound() {
    print('動物在發出聲音...');
  }
}

class Dog extends Animal {
  Dog(String name, int age) : super(name, age);
  // 也可以寫成以下的樣子
  // Dog(super.name, super.age);

  @override
  void makeSound() {
    print('$name 在汪汪叫!');
  }

  void fetch() {
    print('$name 在撿球!');
  }
}

class Cat extends Animal {
  Cat(super.name, super.age);

  @override
  void makeSound() {
    print('$name 在喵喵叫!');
  }
}

void main() {
  Dog myDog = Dog('小黑', 3);
  Cat myCat = Cat('小白', 2);

  myDog.makeSound(); // 小黑 在汪汪叫!
  myDog.fetch(); // 小黑 在撿球!

  myCat.makeSound(); // 小白 在喵喵叫!
}

Mixin

參考官方文件:https://dart.dev.org.tw/language/mixins

在 Dart 中,與 Java 一樣,不允許一次繼承多個 class,若要使用多重繼承,可以使用 mixin。一個類別可以 extends 另一個 class,以及混合多個 minxin。mixin 本身無法再繼承也不可以實例化。

mixin Flyable {
  void fly() {
    print('我在飛!');
  }
}

class Animal {
  void eat() {
    print('我在吃!');
  }
}

class Bird extends Animal with Flyable {
  // Bird 繼承 Animal,並混入 Flyable 的行為
}

void main() {
  Bird bird = Bird();
  bird.eat(); // 我在吃!
  bird.fly(); // 我在飛!
}

觀察上述範例,假如,AnimalFlyable 同時具有一個相同名稱的方法時,究竟會先用調用哪一個呢?在 Dart 中,會先從最右邊的 mixin 開始向左尋找,最後才會像父母類別尋找。

mixin Flyable {
  void something() {
    print("Flyable");
  }
}

mixin Jumpable {
  void something() {
    print("Jumpable");
  }
}

class Animal {
  void something() {
    print("Animal");
  }
}

class Bird extends Animal with Flyable, Jumpable {
  // Bird 繼承 Animal,並混入 Flyable 的行為
}

void main() {
  Bird bird = Bird();
  bird.something(); // Jumpable
}

在 Dart 中,我們也可以限定 mixin 僅能對特定的類別使用,比如,我們可以限定 Flyable 僅對 Animal 做用。此時僅需在原本的 minxin 後加入 on 關鍵字即可。

mixin Jumpable on Animal {
  // ...
}

class Bird extends Animal with Flyable { // OK
  // ...
}

// 'Flyable' can't be mixed onto 'Geometry' because 'Geometry' doesn't implement 'Animal'.
class Circle extends Geometry with Flyable {
    
}

如果一個 mixin 同時設定 2 個 on 的類別,則代表當有類別要 with 這個 mixin 時,必需同時滿足這兩個類別。

以下是一個錯誤示範

class A {
  // ...    
}

class B {
  // ...
}

mixin C on A, B {
  // ...
}

// ERROR
class D extends A with C {
    
}

以下是一個正確示範

class A {
  // ...    
}

class B extends A {
  // ...
}

mixin C on A, B {
  // ...
}

// OK 因為此時 D 同時具有 A, B, D 的類別
class D extends B {
    
}

在 Flutter 中何時會使用到 mixin 呢?其實一般情況下都不太會用到,但在處理一些動畫時有可能會用到 SingleTickerProviderStateMixinTickerProviderStateMixin

Implement & Interface class

9/8 補充,參考官方文件:https://dart.dev.org.tw/language/class-modifiers

在 Dart 中,可以直接把 class 當作 Java 中的 interface 使用。也就是讓某個 class 去實作 interface 中所有的方法。我們可以利用 implement 關鍵字將 class 當作是 interface 來實作

class A {
  void a() {
    print("A.a()");
  }
}

class B {
  void b() {
    print("B.a()");
  }
}

class C {
  void c() {
    print("C.c()");
  }
}

class D extends C implements A, B {
  @override
  void a() {
    print("D.a()");
  }

  @override
  void b() {
    print("D.b()");
  }
}

void main() {
  var d = D();
  d.a(); // D.a()
  d.b(); // D.b()
  d.c(); // C.c()
}

在舊版的 Dart,沒有 interface 這個關鍵字。Dart 3 開始,增加了 interface 這個「修飾詞」,我們可以設定一個 class 為 interface class。在沒有 interface 修飾的情況下,我們可以發現對於 class A 是可以被 extends 也可以被 implements

class E extends A {}

void main() {
  var e = E();
  e.a(); // A.a()
}

然而若我們使用 interface class,則會限制 class AA 僅能被 implements 而無法被 extends

interface class AA {
  void aa() {
    print("AA.aa()");
  }
}

// interface class AA 只能被實作
class F implements AA {
  @override
  void aa() {
    print("F.aa()");
  }
}

void main() {
  var f = F();
  f.aa(); // F.aa()

  // 雖然 AA 是 interface class 但確可以被實例化
  var aa = AA();
  aa.aa(); // AA.aa()
}

然而 interface class 和 Java 中的 interface 還是有那麼一點不一樣,因為 Dart 中的 interface class 是可以被實例化的。若我們希望 AA 不能被實例化,我們可以再加入 abstract 關鍵字。

abstract interface class AAA {
  void aaa() {
    print("aaa");
  }
}

void main() {
  // Error!
  // Abstract classes can't be instantiated.
  // var aaa = AAA();
}

在 Dart 中 ListIterator 都是一個 abstract interface class,代表沒有辦法被實例化

abstract interface class List<E> implements Iterable<E>, _ListIterable<E> {
  // ...
}

abstract interface class Iterator<E> {
  // ...
}

Iterable 則是一個 abstracat mixin class

abstract mixin class Iterable<E> {
  // ...
}

abstract 修飾詞代表 Iterable 無法被實例化,mixin 則代表這個 class 可以被 with 所使用。

withimplement 看似類似,但還是有差別,implement 某個 class 必需得要 override 其中的方法否則無法編譯過;而 with 則可以選擇不 override 而是使用繼承的方式繼承方法。


後記:今天去參加 Flutter Formosa 2024 很開心


上一篇
Day-5 Dart 簡介(4):Class, Abstract Class
下一篇
Day-7 Dart 簡介(6):錯誤處理、套件、異步處理、Future及Isolate
系列文
從零開始以Flutter打造跨平台聊天APP30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言