Dart建構函式介紹

Realank Liu發表於2019-07-30

介紹

因為Flutter和Dart語言非常容易上手,很多學習Flutter的同學,一般都會直接寫Flutter頁面,對Dart語言也是一知半解,包括我自己。

Dart語言和很多現代語言都很類似,但是其建構函式,其靈活性,還是比我瞭解的其它語言要多的,所以在寫建構函式的時候,要麼莫名其妙報錯,要麼寫的很複雜,沒有將其精髓掌握。

下面跟大家總結一下Dart建構函式的語法,方便大家查閱

語法介紹

格式

Dart建構函式有4種格式:

  • ClassName(...) //普通建構函式
  • Classname.identifier(...) //命名建構函式
  • const ClassName(...) //常量建構函式
  • factroy ClassName(...) //工廠建構函式

使用

在定義自己的建構函式之前,我們會接觸到建構函式的使用,其用法如下:

var p1 = Point(2, 2); //Dart2中,可以省略建構函式前的new
var p2 = Point.fromJson({'x': 1, 'y': 2});
var p = const ImmutablePoint(2, 2); //常量建構函式,用來建立編譯期常量
複製程式碼

插播廣告
如果想知道某個變數屬於哪個類,可以使用runtimeType:
print('The type of a is ${a.runtimeType}');

定義

1. 預設建構函式

如果你定義了一個類,而沒有定義建構函式,那麼它將有一個預設的建構函式,這個建構函式 沒有引數

如果這個類有父類,那麼預設建構函式,還會呼叫父類的無引數建構函式。

2. 普通建構函式

這就是我們普通的建構函式,其樣子和其它語言幾乎一樣

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;
  }
}
複製程式碼

上例中只有兩個成員變數,如果有10個,豈不是麻煩死?所以Dart有語法糖給你哦:

class Point {
  num x, y;

  // Syntactic sugar for setting x and y
  // before the constructor body runs.
  Point(this.x, this.y);
}
複製程式碼

它可以將x,y的賦值變得簡單一些,就不用寫建構函式的方法體了,記得括號後用分號哦。

3. 命名建構函式


class Point {
  num x, y;

  Point(this.x, this.y);

  // 命名建構函式,新增程式碼
  Point.origin() {
    x = 0;
    y = 0;
  }
}

複製程式碼

請記住,命名建構函式不可繼承,如果子類想要有 和父類一樣的命名建構函式,那就寫個同名的(通常也會在子類的命名建構函式裡,呼叫父類的同名命名建構函式)

4. 建構函式呼叫父類建構函式的順序

如果你的類,繼承於父類,那麼子類的建構函式,勢必要呼叫父類的建構函式,這時候就要分兩種情況:

  • Dart語言幫你呼叫父類的無引數建構函式
  • 程式碼中顯式呼叫父類的建構函式
4.1 預設呼叫呼叫父類的無引數建構函式

如果你沒有顯式呼叫父類的建構函式,並且父類有一個無引數建構函式,那麼Dart就會幫你在子類的建構函式方法體的最前面,呼叫父類的無引數建構函式。當然,後面我們會說道,建構函式分成好幾部分來初始化成員變數,呼叫的順序如下:

  • 初始化列表
  • 父類的無引數建構函式
  • 子類的無引數建構函式

當然,如果父類沒有無引數建構函式,或者Dart這種隱式呼叫無法滿足你的要求,那就需要顯式呼叫父類的建構函式了

4.2 顯式呼叫父類建構函式

顯式呼叫父類建構函式,應該在初始化列表中完成(記得好像在C++中見到過初始化列表?太久了忘記了)

class Person {
  String firstName;

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

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');
  }
}

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

  // Prints:
  // in Person
  // in Employee
}
複製程式碼

初始化列表就是建構函式名的冒號後面,打括號前面的部分。

5. 初始化列表

初始化列表的執行順序,在整個建構函式的最前面,它除了可以呼叫父類的建構函式,還可以在建構函式方法體之前,初始化一些成員變數。

// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}
複製程式碼

尤其是初始化那些final修飾的成員變數時,初始化列表很有用,因為在方法體中,不能給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);
}
複製程式碼

6. 建構函式傳遞

定義建構函式的時候,除了一個普通建構函式,還可以有若干命名建構函式,這些建構函式之間,有時候會有一些相同的邏輯,如果分別書寫在各個建構函式中,會有些多餘,所以建構函式可以傳遞。

class Point {
  num x, y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(num x) : this(x, 0);
}
複製程式碼

傳遞建構函式,沒有方法體,會在初始化列表中,呼叫另一個建構函式。

7. 常量建構函式

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

  final num x, y;

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

如果你的類,建立的物件永遠不會改變,你可以在編譯期就建立這個常量例項,並且定義一個常量建構函式,並且確保所有的成員變數都是final的。

8. 工廠建構函式

有時候可能有一種需求,並不需要每次都建立新的類例項,而是每一種情況,只需要一個例項:

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

  // _cache is library-private, thanks to
  // the _ in front of its name.
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

main() {
    var logger = Logger('UI');
    logger.log('Button clicked');
}
複製程式碼

工廠建構函式,沒有權利訪問this

上例的意思是,類中又一個靜態快取_cache儲存著一些Logger類例項,建立例項時,給工廠建構函式傳遞的name,如果在快取中已經存在,就用快取中現成的例項,如果沒有,就新建一個例項,並且也放到快取中。

如此這般,我們可以建立名字為UI / SYS / API 等的例項,然後在debug的時候,如果設定名字為UI的Logger例項的mute為true,就不會列印UI相關的log,而不影響其它兩個名字的log。是不是很方便呢?

相關文章