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
官方教學文件: 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(); // 小白 在喵喵叫!
}
參考官方文件: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(); // 我在飛!
}
觀察上述範例,假如,Animal
和 Flyable
同時具有一個相同名稱的方法時,究竟會先用調用哪一個呢?在 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
呢?其實一般情況下都不太會用到,但在處理一些動畫時有可能會用到 SingleTickerProviderStateMixin
或 TickerProviderStateMixin
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 中 List
及 Iterator
都是一個 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
所使用。
with
和 implement
看似類似,但還是有差別,implement
某個 class 必需得要 override
其中的方法否則無法編譯過;而 with
則可以選擇不 override
而是使用繼承的方式繼承方法。
後記:今天去參加 Flutter Formosa 2024 很開心