延迟初始化

late 关键字的本意是延迟变量的初始化。

// ch0403_1.dart

// int x;
// Error:
// The non-nullable variable 'x' must be initialized.

late int x; // 1

void main() {
  x = 1; //2
  print(x); // Output: 1
}
  1. 声明int类型的变量 x 并用 late 关键字修饰它,告诉编译器暂时不对x进行初始化;如果去掉这里的 late将引起编译错误 The non-nullable variable 'x' must be initialized
  2. 在使用 x之前对其赋值。

什么时候使用 late

下面按应用场景对late的使用举例说明。

需要使用 non-nullable 变量,但在声明时又无法初始化(比如依赖另一个实例变量/方法 )。

// ch0403_2.dart
class A {
  int x;
  late int y = x + 1;
  A(this.x);

  @override
  String toString() => '($x, $y)';
}

void main() {
  print(A(2)); // Output: (2, 3)
}

此例中变量 y 由变量 x计算而成,用late修饰再方便不过了。

变量的初始化是个费时操作,希望延迟初始化它。

// ch0403_3.dart
import 'dart:math';

class RandAvg {
  int n;
  late double avg;

  RandAvg(this.n);

  static final _rand = Random();

  void init() {
    var s = 0.0;
    for (var i = 1; i <= n; i++) {
      s += _rand.nextDouble();
    }
    avg = s / n;
  }
}

void main() {
  var a = RandAvg(1000_000);
  a.init();
  print(a.avg);
}

此例中的 avgn 个随机浮点数(取值0-1)的算术平均数,其计算过程由 init() 函数完成。

变量仅在程序的部分地方用到,没用到的地方无需初始化。

// ch0403_4.dart
import 'dart:io';

const kDebug = true;

class Logger {
  final String name;
  late String _debugInfo;

  Logger(this.name);

  void ensureDebugInfo() {
    if (kDebug) {
      _debugInfo =
          'OS=${Platform.operatingSystem} CPU cores=${Platform.numberOfProcessors}';
    }
  }

  void log(String message) {
    if (kDebug) {
      print(_debugInfo);
    }
    print('${DateTime.now()} $name: $message');
  }
}

void main() {
  var logger = Logger('main');
  logger.ensureDebugInfo();
  logger.log('Hi Dart');
}

仅当 kDebug 取值 true 时, Logger类的_debugInfo 才被 log 方法用到, 也才被初始化(由ensureDebugInfo方法完成)。

联合使用 late final

示例 ch0403_3.dart 中的实例变量 avg,在初始化之后便不再改变,这样的变量使用 late final 修饰更为准确,可以有效防止二次计算。

// ch0403_5.dart
class RandAvg {
  int n;
  late final double avg;

  RandAvg(this.n);
}