前言:樓主平時基本沒有使用過異常處理,所以對異常的認知可能不夠准確,這里就不翻譯異常的相關內容了,大家可以去官網自行閱讀介紹,地址 https://dart.dev/guides/language/language-tour#exceptions 。
我們前面提到,dart是面向對象的語言,程序中的每個對象都可以看作是某個類的實例。所有的類都派生自Object類。
(一)使用類的成員
與C++等面向對象語言一樣,dart的類成員包括方法(接口)與數據,使用點號操作符(.)可以訪問類的方法和數據。看個簡單例子:
//實例化Point類的一個對象,命名為p var p = Point(2, 2); // 設置類的成員y的值. p.y = 3; // 獲取成員y的值 assert(p.y == 3); // 調用類的distanceTo方法 num distance = p.distanceTo(Point(4, 4));
一個小技巧: 使用 ?. 可以避免訪問一個空實例,如下例:
// If p is non-null, set its y value to 4. p?.y = 4;
(二)構造器
了解C++的朋友都知道,在C++中類一定有構造函數,dart也是如此。
在dart中使用構造器創建一個對象,構造器可以是ClassName
or ClassName.identifier,看以下例子:
// 使用 ClassName 創建 var p1 = Point(2, 2); // 使用 ClassName.identifier 創建 var p2 = Point.fromJson({'x': 1, 'y': 2});
也可以使用 new 關鍵字創建,如下:
var p1 = new Point(2, 2); var p2 = new Point.fromJson({'x': 1, 'y': 2});
以上兩段代碼的效果是一樣的。
(題外話:在C++中使用new創建的是指針,在這里我們看到,以上兩段代碼返回都是var類型)
可以使用const關鍵字創建編譯時常量的對象,簡單例子:
var a = const ImmutablePoint(1, 1); var b = const ImmutablePoint(1, 1); assert(identical(a, b)); // They are the same instance!
接着再看如下例子:
var a = const ImmutablePoint(1, 1); // Creates a constant var b = ImmutablePoint(1, 1); // Does NOT create a constant assert(!identical(a, b)); // NOT the same instance!
通過以上兩段代碼我們可以看出:即便構造器調用參數完全相同,const和非const對象也是不等同的。
(三)獲取一個對象的類型
可以調用對象的 runtimeType 屬性獲取對象的運行時數據類型, 該屬性是 Type 類型的對象。
print('The type of a is ${a.runtimeType}');
(四)實例變量
先看一個例子:
class Point { num x; // 聲明實例變量x num y = 2; // 聲明實例變量y } void main() { var p = Point(); // 構造Point類的實例p print("y = ${p.y}"); if(p.x == null) { print("p.x == null"); } else { print("eeee"); } }
代碼運行結果
通過上例我們可以看出:類中的變量默認被初始化為null,如果我們在類聲明時對變量進行了初始化(如Point類中的y),則該初始化賦值發生在調用構造函數之前!
(五)構造函數
我們可以通過聲明一個與類名完全相同的函數來聲明構造函數(和C++一樣),當然,也可以選擇附加標識符,如 (二)構造器 一段所述。看個例子:
class Point { num x, y; Point(num x, num y) { // There's a better way to do this, stay tuned. this.x = x; this.y = y; } }
這里的this關鍵字指當前實例自身,另注:只在名字有沖突的時候才適用this關鍵字,否則可以省略。
(1)默認構造函數
前面已經提到,和C++一樣,如果你沒有聲明自己的構造函數,那么編譯器會給你提供一個默認的構造函數,默認構造函數無入參,並且調用父類的無參構造函數。
(2)構造函數不能繼承
子類不能繼承父類的構造函數。前面提到,子類如果沒有聲明構造函數,那么會有默認的構造函數,而不是集成父類的。
(3) 命名構造函數 (Named constructor)
看個例子:
class Point { num x, y; Point(this.x, this.y); // Named constructor Point.origin() { x = 0; y = 0; } }
記住,構造函數不能繼承!如果想用父類中的命名構造函數創建一個子類,那你也必須在子類中實現這個命名構造函數!
(4) 構造函數的調用順序
子類的構造函數中執行的操作順序如下:
初始化列表->父類的無參構造函數->本類的無參構造函數。
如果父類沒有非命名、無參數的構造函數,那么你就必須手動調用父類的一個構造函數,在分號(:)后指明調用的父類構造函數,看以下例子:
class Person { String firstName; Person.fromJson(Map data) { print('in Person Class'); } } class Employee extends Person { // Person does not have a default constructor; // you must call super.fromJson(data). Employee.fromJson(Map data) : super.fromJson(data) { print('in Employee Class'); } } main() { var emp = new Employee.fromJson({}); // Prints: // in Person Class // in Employee Class if (emp is Person) { // Type check emp.firstName = 'Bob'; print("emp is Person, firstname = ${emp.firstName}"); } (emp as Person).firstName = 'Bob'; print("Other,firstname = ${emp.firstName}"); }
以上代碼在vs code中運行結果如下:
父類構造函數的參數中無權限使用this關鍵字!
(5) 重定向構造函數
這個重定向構造函數,個人理解有一點點像C++里虛基的構造函數,重定向構造函數函數體為空,僅僅是在該類里用來重定向其他的構造函數的。看一個例子:
class Point { num x, y; // Point類的主構造函數,也是一個重定向構造函數,你看它的函數體是空的 Point(this.x, this.y); // alongXAxis函數被重定向到Point執行構造處理 Point.alongXAxis(num x) : this(x, 0); }
(6)其他
官方對於構造函數還有一些其他類型的描述,如constant構造、factory構造等,本人對此不甚理解,因此這里就不做翻譯,大家有興趣請去官網閱讀相關說明。