寫在前面
最近在折騰 flutter 相關的東西,所以當然要擼一下 dart 了。程式語言這個東西,接觸得多了學習起來速度會提升不少,但是不同的語言具有不同的特色,我們需要花一些時間去關注它們的賣點,而且對於大部分面嚮物件語言,也需要格外注意類的概念,因此專門花了一些時間結合官方文件整理學習 dart 中關於類的內容。
dart 是一門物件導向的語言,既然是物件導向就不會缺少類(class)這個概念。dart 中的 classes 包含的內容繁多,但是如果你同時擁有使用靜態語言和動態語言的經驗則會容易不少。
Note: 示例程式碼中包含一些 dart 的基本語法,建議閱讀之前先進行了解。如果有 typescript 或者 java 使用經驗的話,應該會很熟悉。
宣告、例項化及訪問屬性
這一部分是最基本的內容,和大部分程式語言的語法差不多。比如想要宣告一個 Point 類:
class Point {
num x, y = 1;
Point(num x, num y) {
this.x = x;
this.y = y;
}
Point.fromJson(Map<String, num> json){
this.x = json[`x`];
this.y = json[`y`];
}
/* 類似 typescript 可以使用如下的語法糖
Point(this.x, this.y);
*/
}
例項化:
Point p = Point(1, 1); // 或者 new Point(1, 1)
訪問屬性:
print(`${p.x} ${p.y}`); // 類的方法同理
還可以使用 ?. 來避免當訪問例項為空時丟擲異常:
print(`${p?.x}`);
屬性可見範圍
dart 中不存在類似 java 和 typescript 中的 private、protected、public 修飾符,它使用約定來對類屬性的可見範圍進行控制。約定如下:
如果一個識別符號以下劃線(_)開頭,則它為一個私有識別符號。
建構函式
dart 類的建構函式存在兩種形式,一種為 ClassName() ,另一種是 ClassName.ConstructorName() ,舉例說明:
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({`x`: 1, `y`: 2});
這裡的 fromJson 是一個自定義的構造器方法,在 dart 中它叫做 Named constructors,所以上面的 Point 類也可以這麼宣告:
class Point {
num x, y = 1;
Point(num x, num y) {
this.x = x;
this.y = y;
}
Point.fromJson(Map<String, num> json){
this.x = json[`x`];
this.y = json[`y`];
}
}
在 dart 的構造器中還涉及一個東西,叫作 initializer list。大體的語法如下:
class Point {
...(略)
Point.fromJson(Map<String, num> json): x = json[`x`],y = json[`y`];
}
這種寫法和上面的程式碼是等價的。關於 initializer list 語法可以參考這裡。
除了基本的構造器以外,dart 還可以宣告其他型別的構造器,當前有三種:
關於具體的語法可以參考連結所指向的官方文件,我覺的比較有用的應當是工廠構造器。因為在物件導向程式設計中,一個基本的設計模式即是工廠模式,dart 提供的工廠構造器可以說是在語法層面原生提供工廠模式的實現方式。
最後關於構造器還有一點值得一說,就是當存在繼承關係並在預設情況下,構造器的呼叫順序如下:
initializer list -> 父類預設無參構造器 -> 主類預設無參構造器
如果父類不存在預設無參構造器,那麼主類必須顯式地呼叫父類的其他構造器(Named constructors 或者 有參構造器),呼叫的程式碼可以包含在 initializer list 中,如下:
class Employee extends Person {
Employee(Map data) : super(data);
}
方法
類的方法可以劃分為以下幾類:
- 例項方法
- getter/setter
- 抽象方法(必須在抽象類中)
介面
不像 java,dart 中每一個類都會隱式的宣告一個包含當前類及它所實現所有介面的成員屬性的介面。現在我們想實現一個可以 run 和 jump 的 Person 類,程式碼如下:
class Run {
void run() {}
}
class Jump {
void jump() {}
}
class Person implements Run, Jump {
void run() {
print(`I can run`);
}
void jump() {
print(`I can jump`);
}
}
繼承
和其他物件導向程式語言中的繼承差不多,可以參考這裡。
列舉
dart 中也可以像 typescript 一樣,使用 enum 宣告列舉物件,如下:
enum Color { red, green, blue }
列舉相比類有如下限制:
無法繼承或者使用 mixin,同時也無法被當做介面
無法顯示例項化
mixins
熟悉 python 的話會很熟悉這個特性,dart 中使用 with 關鍵字來在一個類中混入 mixins,比如:
class Musician extends Performer with Musical {
// ···
}
宣告一個 mixin 的語法很簡單,首先創造一個抽象類,同時不要宣告構造器,如下:
abstract class Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
...(做一些事)
}
}
靜態屬性及方法
和其他程式語言類似,只需要在屬性或者方法前加 static 關鍵字即可。
抽象類
和其他程式語言類似,通過 abstract 關鍵字宣告,比如:
abstract class AbstractContainer {
void updateChildren(); // Abstract method.
}
抽象類無法例項化,除非它被實現。
Callable
類可以提供一個 call() 方法以使當前類成為 Callable class,提供該方法以後類例項可以被當做函式來呼叫,比如:
class Point {
...(略)
call() => `${this.x} ${this.y}`;
}
直接呼叫類例項來輸出座標:
var p1 = new Point(1, 2);
p1(); // 1 2