基礎型

先來最簡易的版本,不管從任何語言背景來的人都能看懂。
另外要注意在dart 2.0以後,new變成了可選字眼,所以為了程式簡潔,個人建議不要再加new。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Cat{
  int age;
  Cat(int age){
    this.age = age;
  }
}
// 使用時
var cat = Cat(1);
// or 舊版寫法,2.0後亦相容。
var cat = new Cat(1);

延伸一點,其實參數可以直接使用this.fieldName來進行初始化。
可以減少許多冗餘的程式碼。

1
2
3
4
5
6
class Cat{
  int age;
  Cat(this.age);
}
// 使用時
var cat = Cat(1);

如果想在填參數時能夠直接參照fieldName,可以將參數用大括弧括起。
這種做法叫做 Optional named parameters,顧名思義,在初始化時甚至可以不給值。

1
2
3
4
5
6
7
8
9
class Cat{
  int age;
  Cat({this.age});
}

// 使用時
var cat = Cat(age=1);
// or 可以不給值
var cat = Cat();

另外要注意constructor是不會繼承的,也就是繼承後的物件僅有default constructor。

Named constructors

Dart是沒有function overloading的,所以若想擁有不同constructor,就需要對這些constructor進行命名。

1
2
3
4
5
6
7
8
9
class Cat{
  int age;
  Cat.baby(){
    age = 0;
  }
}

// 使用時
var babyCat = Cat.baby();

另外如果想要給class field初始值,還有以下幾種方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Cat{
  // 適用於有多個constructor又想指定為同一個初始值時
  int age = 0;
  Cat.baby();
}

class Cat{
  // 適用於有多個constructor但想指定不同初始值時
  int age;
  Cat.baby(): age=0;
  Cat.baby2(): age=0 {
    //這裡還可以再加寫東西
  };
}

class Cat{
  // 適用於Optional named parameters
  int age;
  // 新版寫法
  Cat.baby({this.age=0});
  // 舊版寫法,可能會被廢棄,不再建議使用。
  Cat.baby2({this.age:0});
}

值得注意的是,如果field為final,則第一種(在大括號內初始化)的方法就不再適用,編譯器會報出錯誤訊息。
另外這些初始化方法不管是基礎型constructor或是Named constructor皆適用。

const constructor

這是一個比較難懂的概念,可以先參看下列的範例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Cat{
  final int age;
  const Cat(this.age);
}

// 使用時
var a = const Cat(1);
var b = const Cat(1);
assert(a == b); // true

// 錯誤使用
var c = Cat(1);
var d = Cat(1);
assert(c == d); // error

簡單的說,a跟b為同樣的instance,所以在進行==比較時回傳結果為true。而c跟d為不同的instance,在進行比較時回傳結果為false。
a跟b明明都是由constructor所產生,但卻產生了一樣的instance,因此這個constructor被稱為const constructor。

現在我們來看一下要使用const constructor的規則。

  1. class內的所有成員皆為final。
  2. 被宣告為const的constructor一定要初始化所有變數。
  3. 要使用const constructor時,必須明確的在constructor前再次加上const。(e.g. var cat = const Cat(1);)
  4. 使用const constructor時,所有傳入的參數必須要是常數(constant),否則編譯器會報錯。
  5. 若使用const constructor時沒加上const前綴,也可以當作一般的constructor用。(e.g. var cat = Cat(1);)

因此宣告ㄧ個const constructor,並不代表使用這個constructor的結果都為const constructor,這是最大的陷阱。
下面再多給幾個範例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 可行,傳入變數為constant
const a = 1;
var cat = const Cat(a);

// 不可行,final並非constant
final a = 1;
var cat = const Cat(a); // error

// 可行,不加上const前綴則當作一般的constructor
final a = 1;
var cat = Cat(a);

factory constructor

最後一個要提的constructor是factory constructor,顧名思義,此為factory pattern的語法糖。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Cat{
  int age;
  factory Cat.baby(){
    final cat = Cat();
    cat.age = 0;
    return cat;
  }
}

// 使用時
var cat = Cat.baby();

這個範例可以很清楚的看到,factory constructor的instance製造是仰賴其他constructor,而且最後還必須return instance。
所以factory constructor根本不是constructor,而是一個factory,只是用個語法糖讓他看起來像constructor罷了。

Reference