1.獲取運行時對象類型
使用Object 屬性的 runtimeType,它返回一個 Type 對象。
print('a 的類型是 ${a.runtimeType}');
??警告
在測試對象的類型時建議使用object is Type
比測試 object.runtimeType == Type 更穩定。
2.實例變量的聲明
class Point {double? x; // 聲明實例變量 x,初始值為 null。double? y; // 聲明 y,初始值為 null。double z = 0; // 聲明 z,初始值為 0。
}
所有實例變量都會生成一個隱式的 getter 方法。非final的實例變量,以及未初始化的late final實例變量,都會生成一個隱式的setter方法。
class Point {double? x; // 聲明實例變量 x,初始值為 null。double? y; // 聲明 y,初始值為 null。
}void main() {var point = Point();point.x = 4; // 使用 x 的 setter 方法。assert(point.x == 4); // 使用 x 的 getter 方法。assert(point.y == null); // 默認值為 null。
}
在聲明時初始化非 late 實例變量會在實例創建時設置值,在構造函數及其初始化列表執行之前。因此,非 late 實例變量的初始化表達式(= 之后的表達式)無法訪問 this。
double initialX = 1.5;class Point {// 可以訪問不依賴 `this` 的聲明:double? x = initialX;// 錯誤,在非 `late` 初始化器中無法訪問 `this`:double? y = this.x;// 可以,在 `late` 初始化器中訪問 `this`:late double? z = this.x;// 可以,`this.x` 和 `this.y` 是參數聲明,不是表達式:Point(this.x, this.y);
}
實例變量可以是 final 的,在這種情況下它們必須被設置且僅設置一次。可以在聲明時
、使用構造函數參數
或者使用構造函數的初始化列表
來初始化final類型的非late實例變量。
class ProfileMark {final String name;final DateTime start = DateTime.now();ProfileMark(this.name); //使用構造函數參數,來初始化final類型的非late實例變量ProfileMark.unnamed() : name = ''; //使用構造函數的初始化列表,來初始化final類型的非late實例變量
}
如果您需要在構造函數體開始后為 final 實例變量賦值,可以使用以下方法之一:
- 使用工廠構造函數。
- 使用
late final
,但要小心:沒有初始值設定器的late final
會為 API 添加一個 setter。
3.隱式接口
每個類都隱式地定義了一個接口,該接口包含該類的所有實例成員以及它所實現的任何接口的實例成員。如果你想創建一個支持類 B 的應用程序編程接口(API)但又不繼承 B 的實現的類 A,那么類 A 應該實現 B 接口。
implements
關鍵字主要用于類實現接口
-
概念:接口規定了一組方法的簽名,不過不包含具體實現。類借助 implements 關鍵字來實現接口時,就必須實現接口里定義的所有方法
-
使用方法:在類定義時,運用 implements 關鍵字,后面緊跟一個或多個接口名,若有多個接口,用逗號分隔。類需要實現接口中定義的所有方法。
-
示例
// 定義一個接口
abstract class Animal {void eat();void sleep();
}// 實現接口的類
class Dog implements Animal {void eat() {print('Dog is eating.');}void sleep() {print('Dog is sleeping.');}
}void main() {Dog dog = Dog();dog.eat();dog.sleep();
}
在上述代碼中,Animal 是一個接口,定義了 eat 和 sleep 方法。Dog 類使用 implements 關鍵字實現了 Animal 接口,并且實現了接口中定義的所有方法。
與 extends 的區別
- extends:用于類的繼承,一個類只能單繼承,子類會繼承父類的實現。
- implements:用于實現接口,一個類可以實現多個接口,類需要自己實現接口中定義的所有方法
一個類通過在 implements 子句中聲明一個或多個接口,然后提供這些接口所要求的 API 來實現它們。
以上來自AI的解釋,下面是dart官方文檔的
類通過在 implements 子句中聲明一個或多個接口并提供接口所需的 API 來實現它們。例如:
// 一個 person類. 包含一個隱式接口greet().
class Person {// 在接口中,但僅在此類中可見.final String _name;// 不在接口中,因為這是一個構造函數.Person(this._name);// 在接口中.String greet(String who) => 'Hello, $who. I am $_name.';
}// Person 接口的一個實現類.
class Impostor implements Person {String get _name => '';String greet(String who) => 'Hi $who. Do you know who I am?';
}String greetBob(Person person) => person.greet('Bob');void main() {print(greetBob(Person('Kathy'))); //你好,Bob。我是 Kathyprint(greetBob(Impostor())); //嗨 Bob。你知道我是誰嗎?
}
4.類變量和方法
使用 static 關鍵字來實現類級的變量和方法。
- 靜態變量
靜態變量(類變量)對于類范圍的狀態和常量非常有用
class Queue {static const initialCapacity = 16;// ···
}void main() {assert(Queue.initialCapacity == 16); //靜態成員/方法無需創建實例,直接訪問/調用
}
靜態變量只有在使用時才會被初始化
- 靜態方法
靜態方法(類方法)不作用于實例,因此無法訪問 this。但是,它們可以訪問靜態變量。如下例所示,您直接在類上調用靜態方法:
import 'dart:math';class Point {double x, y;Point(this.x, this.y);static double distanceBetween(Point a, Point b) {var dx = a.x - b.x;var dy = a.y - b.y;return sqrt(dx * dx + dy * dy);}
}void main() {var a = Point(2, 2);var b = Point(4, 4);var distance = Point.distanceBetween(a, b);assert(2.8 < distance && distance < 2.9);print(distance);
}
??注意
對于常用或廣泛使用的工具和功能,考慮使用頂級函數,而不是靜態方法。
您可以將靜態方法用作編譯時常量。例如,您可以將靜態方法作為參數傳遞給常量構造函數。