iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 14
0

什麼是類別 (Class)?

在物件導向語言中,每一個物件都是一個類別,子類別繼承父類別以取用父類別的類別方法與類別屬性。在 Dart 中,每一個物件都是 Object,這代表每一個物件都是繼承 Object 類。

雖然,每一個類別都只能繼承一個父類別,不過 Dart 提供了 Mixin-based 的繼承,可以“混合”多個類別在同一個類別。

定義一個類別

那麼,要如何定義一個類別呢?

通常,第一步,我們會建立一個與類別同名的檔案。

例如,我們要建立一個名稱為 Student 的類別,我們會先建立一個 student.dart 的檔案

第二步,在該檔案中,使用關鍵字 class 來建立一個類別。

class ClassName{
	...
}

一般在定義類別的名稱時,都是以駝峰的形式來命名。

範例:Student 類別

class Student {
	var name = 'Andy';
	var age = 30;
	void greeting(){
		print('Hello, my name is $Andy');
	}
}

類別實例化

要使用類別,要先將它實例化才能使用。

範例:實例化 Student 類別。

void main(){
	var student = Student();
}

發現什麼?

將類別名稱後面加上小括弧,就代表實例化了這個類別。

其中,小括弧代表的是建構式傳入的引數,預設的建構式是沒有傳入引數,所以使用空的小括弧即可。

使用類別成員

每一個類別裡,都是由函數 (Functions) 以及屬性 (Properties) 所組成。當外部實例化該類別之後,要怎麼參考該類別裡的成員(函數或屬性)呢?

用點 ( . )來參考成員。

void main(){
	var student = Student();
	var student_name = student.name;
	var student_age = student.age;
	student.greeting();
}

如果類別沒有實例化,是不能參考裡面的函數及屬性,

例如:我們未將 Student 類實例化,卻參考了 name 。


void main(){
  Student student;
  student.name;
}

//Unhandled exception:
//NoSuchMethodError: The getter 'name' was called on null.

此時就會發生異常:NoSuchMethodError: The getter 'name' was called on null。

因為 student 尚未設定值,所以預設是 null,null 是不能參考任何項目的。

在點前方加上一個問號 (?.),代表非 null 時,才參考,否則就跳過。

void main(){
  Student student;
  student?.name;
}

建構式 (Constructor)

在上面的範例中,Student 類裡的屬性:name 以及 age,都是固定值,每次參考的時候,都不會有任何改變,我們將這兩個屬性改成由建構式進行初始化。

範例:替 Student 類加上建構式。

class Student{
	final String name;
	final int age;

	Student(this.name, this.age);
	
	void greeting(){
		print('Hello, my name is $name');
	}
}

發現什麼?

  1. 屬性前方加上 final:代表屬性是由建構式傳入,在類別內無法被修改。如此可以確保我們的屬性不會因為程式而變動。
  2. 建構子的寫法是由類別名稱加上小括弧,裡面填入需要傳入的引數。

範例:實例化含有建構式的 Student 類別

void main(){
	var student = Student('Axl', 30);
	student.greeting();
}
//Hello, my name is 'Axl'

發現什麼?

在實例化時,小括弧裡面直接將數值按照建構式的順序填入。

具名引數 (Named Arguments)

前面所介紹的為預設的建構式,具有順序、數量一致的特性。當順序不對,傳入的內容就不正確、甚至會編譯失敗。

什麼是具名引數呢?在呼叫建構式建立類別實例之時,不需依照順序、數量,也可以成功的建立,但是,在每一個傳入的建構子的引數,都必須提供屬性的名稱。如此一來,編譯器才會知道由具名建構式帶入的引數,是要指定給哪一個屬性。

定義如下:

ClassName({properties...});

範例:

class Student{
    final String name;
    final int age;
    Student({this.name, this.age});
}

發現什麼?

在類別建構子後方的括弧,裡面用大括弧 { } 包起來。

使用範例:

void main(){
	var student = Student(name:'John', age: 10);
}

用具名建構式建立類別時,將類別的屬性用冒號帶入傳入的引數,再用逗號分隔不同的屬性。


具名建構式 (Named Constructors)

當類別裡有預設建構子後,可以針對不同的用途建立新的建構式,並用不同的名稱識別。

ClassName.named(parameters){
	//set property of class
}

如果我們想針對國小一年級的學生設計類別,且因為每個國小一年級的學生年齡都是8歲,如果使用原本的建構式,就會每次都傳入相同的引數:(age=8),有沒有什麼方法可以固定住某個引數呢?

將前面的範例修改如下:

class Student{
    String name;
    int age;

    Student(this.name, this.age);

    Student.first_grade(String name){
			this.name = name;
			this.age = 8;
		}
}

發現什麼?

  1. 移除 final 修飾,因為建立一個新的具名建構式,所以原本的建構式產生時就不會將屬性帶入,那麼該 final 的屬性就會為 null 。這是不被允許的。
  2. 具名建構式的引數不需要與預設建構式的數量相同。我們可以在後方的大括弧內,進行設定屬性的動作。

在上方的範例中,具名建構式設定屬性值的動作是在大括弧內進行,我們可以將大括弧改為直接呼叫預設建構式。

class Student{
    String name;
    int age;

    Student(this.name, this.age);

    Student.first_grade(String name) : this(name, 8);
}

發現什麼?

我們在具名建構式後方加上冒號以及關鍵字 this,這表示要呼叫這個類別裡的建構式。

接者,我們將具名建構式的引數直接傳給預設建構式,再將數字 8 填入第二個欄位。

這樣一來,我們使用具名建構式之後,它就會將值傳給預設建構式,來達到一樣的效果。

工廠建構式 (Factory constructors)

在工廠模式中,我們可以產生一個類別的實例化。

工廠模式的關鍵字 factory。

class Student{
    String name;
    int age;

    Student(this.name, this.age);

    factory Student.first_grade(String name){
				return Student(name, 8);
		}
}

常量建構式 (Constant constructors)

上面的範例,在我們每次建立實例的時候,都會指向記憶體的某一塊區域。雖然帶入的引數相同,在執行時,都會被指向不同的記憶體區塊。

有時候,我們希望帶入的引數相同,在建立類別實例時,這些類別實例會被指向在同一個記憶體位置上。

這時候我們可以用關鍵字 const 來定義建構式。

範例:將 Student 的建構式以 const 修飾。

class Student{
   final String name;
   final int age;

   const Student(this.name, this.age);
}

把它實例化看看,並利用 identical() 檢查兩個物件是否相同。

void main(){
	var student1 = const Student('Alex', 10);
	var student2 = const Student('Alex', 10);

	var isSame = identical(student1, student2);
	print(isSame);
}

//true

identical 的說明如下:

Check whether two references are to the same object.

小結

建構式,是外部的呼叫者與類別溝通的橋樑,有了建構式才能夠設定類別的屬性。

Dart 提供了四種不同的建構式:預設建構式、具名建構式、工廠建構式以及常量建構式。每一種建構式都有不同的使用情境。

預設建構式是一個類的最基本建構式。

具名建構式可以用在一個類的延伸用法,譬如說某些屬性是固定值。

工廠建構式在每一次呼叫的時候,都會建立一個新的實例,我很常使用在收到 Http request 之後
,解析 Json Object 並實例化物件。

常量建構式用在如果需要在相同的引數下指向相同的記憶體位置時。(表示是完全相同的物件,不是只有屬性相同)

最後,我們還可以用具名引數把帶入的引數加上名稱,讓建構式可讀性增加。


上一篇
Day 13:例外處理
下一篇
Day 15:方法(Method)、getter 以及 setter
系列文
Dart 語言 - 開啟 Flutter 的鑰匙30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言