Dart基礎之Classes(類)上篇

前言

Dart是一種面向對象的語言,具備類和混合繼承。 每一個對象都是一個類的實例,全部類都來自Object。 混合繼承意味着雖然每一個類(除了Object)只有一個超類(也稱爲父類),可是類體能夠在多個類層次結構中重用。json

如何使用類成員

對象擁有由函數和數據(分別爲方法和實例變量)組成的成員。在一個對象上調用方法時:該方法能夠訪問該對象的函數和數據。緩存

用逗號(.)去獲取一個實例的變量或者方法:ide

var p = Point(2, 2);

// 設置實例 p 的 屬性 y 爲 3
p.y = 3;

// 獲取實例 p 的屬性 y
assert(p.y == 3);

// 調用實例 p 的 distanceTo()
num distance = p.distanceTo(Point(4, 4));
複製代碼

當最左邊的操做數爲null時,請用?.來代替.以免異常發生函數

// 當 p 爲非空時, 纔會把 y 設置爲 4
p?.y = 4;
複製代碼

利用構造函數

你能夠用構造函數來建立一個對象, 構造函數的名字能夠是 ClassName 或者 ClassName.identifier。例如,下面的代碼用Point()Point.fromJson()構造函數建立Point對象:ui

var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
複製代碼

下面的代碼有相同的做用,可是在構造函數名字前用了一個可選的關鍵字new:this

var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
複製代碼

注意版本:關鍵字new在Dart2中變爲可選的spa

一些類提供了常量構造函數。爲了建立一個編譯時的常量可使用常量構造函數,把const關鍵字放在構造函數名稱前就能夠了:code

var p = const ImmutablePoint(2, 2);
複製代碼

構建兩個相同的編譯時常量會產生一個單個,標準的實例:對象

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // a和b是同一個實例
複製代碼

在一個常量上下文中,你能夠在構造函數或者文字前省略const。例如,看下下面的代碼,建立了一個map常量:繼承

// 茫茫多的 const 關鍵字
const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
複製代碼

你能夠省略除了第一個的其餘const關鍵字:

// 只要一個 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); // 沒有建立一個常量

assert(!identical(a, b)); // 不是同一個實例
複製代碼

注意版本:關鍵字 const在 Dart2 的常量上下文中才變可選

獲取一個對象的類型

在運行時獲取一個對象的類型,你可使用ObjectruntimeType屬性,它會返回一個Type對象。

print('The type of a is ${a.runtimeType}');
複製代碼

看到這裏,你已經知道怎麼去使用classes了。剩下的章節教你如何去實現classes

實例變量

class Point {
  num x; //申明一個實例變量x, 初始值null
  num y; //申明一個y,初始值null
  num z = 0; //申明一個z,初始值0 
}
複製代碼

全部沒有初始化過的實例變量,默認值都是 null

全部實例變量會生成一個隱式的的getter方法。全部的非 final 定義的實例變量會生成一個隱式的setter方法。

class Point {
  num x;
  num y;
}

void main() {
  var point = Point();
  point.x = 4; // 使用了 x 的 `setter` 方法.
  assert(point.x == 4); // 使用了 x 的 `getter` 方法.
  assert(point.y == null); // y 沒有賦值的狀況下默認 `getter` 方法拿到的值是 `null`.
}
複製代碼

若是你在申明時初始化了一個實例變量(代替了在構造函數或者方法中),當實例被建立時就會被賦值,至關於在構造函數和它的初始化列表執行以前。

構造函數

建立一個跟類同名的函數來申明一個構造函數(增長,可選,一個額外的identifier稱爲命名構造函數)。下面是構造函數最普通的模式,製造型的構造函數,用來建立一個類的實例:

class Point {
  num x, y;

  Point(num x, num y) {
    // 有更好的方式實現這種賦值
    this.x = x;
    this.y = y;
  }
}
複製代碼

this關鍵字指的是當前的實例。

注意:只有當存在一個命名衝突時使用this,另外Dart的風格是省略this

經過構造函數給實例變量賦值的方式很是廣泛,因此 Dart 有語法糖讓它變得更簡單:

class Point {
  num x, y;

  // 在構造函數方法體中實現變量的賦值
  Point(this.x, this.y);
}
複製代碼

默認構造函數

即便你不申明一個構造函數,Dart 會默認提供一個給你。默認構造函數沒有參數而且會觸發超類(父類)的無參構造函數。

構造函數不能被繼承

子類不能繼承它們超類的構造函數。一個子類申明無參構造函數只能是默認的(無參數,無命名)構造函數。

命名構造函數

用一個命名構造函數實現多個構造函數,來爲一個類提供更清晰的表達:

class Point {
  num x, y;

  Point(this.x, this.y);

  // 命名構造函數
  Point.origin() {
    x = 0;
    y = 0;
  }
}
複製代碼

請記住,構造函數不是繼承的,這意味着超類的命名構造函數不會被子類繼承。 若是但願使用超類中定義的命名構造函數建立子類,則必須在子類中實現該構造函數。

調用非默認的超類構造函數

默認狀況下,子類中的構造函數調用超類的未命名的無參數構造函數。 超類的構造函數會在構造函數體的開頭被調用。 若是還使用初始化列表,則在調用超類以前執行。 總之,執行順序以下:

  • 初始化列表
  • 超類的無參數構造函數
  • 主類的無參數構造函數

若是超類沒有未命名的無參數構造函數,則必須手動調用超類中的一個構造函數。 在冒號(:)以後指定超類構造函數,就在構造函數體(若是有)以前。

在下面的示例中,Employee類的構造函數爲其超類Person調用命名構造函數。

class Person {
  String firstName;

  Person.fromJson(Map data) {
    print('in Person');
  }
}

class Employee extends Person {
  // Person 沒有一個默認的構造函數
  // 你必須調用 super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

main() {
  var emp = new Employee.fromJson({});

  // 輸出:
  // in Person
  // in Employee
  if (emp is Person) {
    // Type check
    emp.firstName = 'Bob';
  }
  (emp as Person).firstName = 'Bob';
}
複製代碼

由於在調用構造函數以前會計算超類構造函數的參數,因此參數能夠是一個表達式,例如函數調用:

class Employee extends Person {
  Employee() : super.fromJson(getDefaultData());
  // ···
}
複製代碼

警告:超類構造函數的參數無權訪問它。 例如,參數能夠調用靜態方法,但不能調用實例方法。

初始化列表

除了調用超類構造函數以外,還能夠在構造函數體運行以前初始化實例變量。 用逗號分隔初始化程序。

// 初始化列表會在構造函數體運行以前設置實例變量
Point.fromJson(Map<String, num> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}
複製代碼

警告:初始化程序的右側無權訪問this

在開發期間,你可使用初始化列表中的assert來驗證輸入。

Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}
複製代碼

初始化列表時能夠很方便設置final字段,如下示例初始化初始化列表中的三個final字段。

import 'dart:math';

class Point {
  final num x;
  final num y;
  final num distanceFromOrigin;

  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

main() {
  var p = new Point(2, 3);
  print(p.distanceFromOrigin);
}
複製代碼

重定向構造函數

有時構造函數的惟一目的是重定向到同一個類中的另外一個構造函數。 重定向構造函數的主體是空的,構造函數調用出如今冒號(:)以後。

class Point {
  num x, y;

  // 這個類的主構造函數
  Point(this.x, this.y);

  // 委託給了主構造函數
  Point.alongXAxis(num x) : this(x, 0);
}
複製代碼

常量構造函數

若是你的類生成對象後永遠不會更改,那麼可使這些對象成爲編譯時常量。 爲此,請定義const構造函數並確保全部實例變量都是final

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}
複製代碼

工廠構造函數

在實現並不老是建立一個類新實例的構造函數時,請使用factory關鍵字。 例如,工廠構造函數可能從緩存中返回實例,或者它可能返回子類型的實例。

如下示例演示了從緩存中返回對象的工廠構造函數:

class Logger {
  final String name;
  bool mute = false;

  // _cache 是庫私有的
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    return _cache.putIfAbsent(
        name, () => Logger._internal(name));
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}
複製代碼

注意:工廠構造函數沒有權限訪問this

像調用任何其餘構造函數同樣調用工廠構造函數:

var logger = Logger('UI');
logger.log('Button clicked');
複製代碼
相關文章
相關標籤/搜索