Dart官方文檔:https://dart.dev/language/classes
重要説明:本博客基於Dart官網文檔,但並不是簡單的對官網進行翻譯,在覆蓋核心功能情況下,我會根據個人研發經驗,加入自己的一些擴展問題和場景驗證。
Dart類
Dart語言基於類和Mixin繼承,是一門面向對象語言。任何對象都是某個類的實例,除Null之外,Object類其他所有類的父類。
Mixin繼承:Dart語言和Java語言一樣,類只能是單繼承。但通過Mixin,一個類的代碼可以在多個類層次結構中複用(有關Minxin的詳細説明見之前文章:https://ntopic.cn/p/2023093001)。
方法擴展:在不改變原有類和增加子類的情況之下,通過Dart的方法擴展,可以給類增加功能的一種方法(這個特性在Flutter發佈的庫特別有用)。
類修飾符:可以讓我們可控制一個庫如果定義子類。
類成員(方法和變量)
對象是由函數和數據組成,分別代碼方法和變量。我們通過對象.方法或者對象.變量的方法來訪問對象方法和變量。當對象可能為null時,通過對象.?訪問方法和變量的方式可防止異常發生。
// 定義對象
var p = Point(2, 2);
// 獲取對象變量`y`
assert(p.y == 2);
// 調用對象方法:`distanceTo()`
double distance = p.distanceTo(Point(4, 4));
// 當對象`p`非空時,`a`值為變量`y`;否則`a`值為null
var a = p?.y;
類構造函數
在前面學習中,我們對構造函數有初步認識:https://ntopic.cn/p/2023092401
如下代碼樣例,可以通過主構造函數和命名構造函數創建一個對象;構造函數之前,我們也可以增加可選的new關鍵字:
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
// 同上等價代碼,可選的`new`關鍵字
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
當類的變量都用final不可變修飾時,我們可以構造常量對象:
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
// 對象`a`和`b`相等
assert(identical(a, b));
對於一個常量上下文,我們可以去掉構造函數之前的const關鍵字。如下代碼樣例,我們定義的是一個常量Map(上下文),那麼Map元素的構造器就可以省略const關鍵字:
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
// 同上等價代碼,可省略`const`關鍵字
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
如果一個對象沒有常量上下文,且沒有使用const修飾構造器,那麼它創建的是一個非常量對象:
var a = const ImmutablePoint(1, 1);
var b = ImmutablePoint(1, 1);
// `a`是常量對象,`b`不是常量對象,因此它們不相等!
assert(!identical(a, b));
獲取對象類型
通過對象.runtimeType屬性,返回對象的Type對象。一般情況下,我們通過對象 is Type的方法,檢測某個對象是否屬於某個類型,而不是使用對象.runtimeType == Type比較方式:
print('The type of a is ${a.runtimeType}');
var a = 'Hello NTopicCN';
assert(a.runtimeType == String);
assert(a is String);
實例變量
如下代碼定義樣例,申明實例變量,實例變量的默認值為null:
class Point {
double? x; // 默認值:null
double? y; // 默認值:null
double z = 0; // 默認值:0
}
所有的實例變量都隱含有一個getter方法,包括final修飾的變量、未使用final修飾的變量、late final修飾的變量(賦值和未賦值)等,都有getter方法。
如下代碼樣例,幾種實例變量修飾和訪問的方法:
class Point {
double? x;
double? y;
}
void main() {
var point = Point();
point.x = 4; // `setter`方法賦值
assert(point.x == 4); // `getter`方法取值
assert(point.y == null); // 默認值為`null`
}
class ProfileMark {
final String name;
final DateTime start = DateTime.now();
// 主構造函數
ProfileMark(this.name);
// 命名構造函數,同時`name`設置初始值
ProfileMark.unnamed() : name = '';
}
隱性接口
在Dart中,每個類都隱含的定義了一個接口,這個接口包含了該類的所有實例成員和該類實現的所有的其他接口。
假設我們定義了一個類A,它需要支持類B的API(構造函數不是API),但是類A的定義並不是繼承類B,那麼類A需要實現B接口。
// `Person`類,也是`Persion`接口,包含`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'))); // Hello, Bob. I am Kathy.
print(greetBob(Impostor())); // Hi Bob. Do you know who I am?
}
類變量和方法
static關鍵字,可以定義類變量和方法(Java中成為靜態變量和靜態方法)。
如下代碼樣例,定義和使用一個類變量:
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
assert(Queue.initialCapacity == 16);
}
如下代碼樣例,定義和使用類方法:
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);
}
最佳實踐:對於一些常用的工具方法,建議使用頂級方法代替類變量。
類方法可以用作編譯期常量,比如:我們可以把一個類方法當作參數傳遞給常量構造器。
我的本博客原地址:https://ntopic.cn/p/2023102001