Dart: Class

 14th December 2020 at 10:14am

Dart 继承模型:单继承、多 mixin

带条件地访问成员变量

// p 为 null 时 a 为 null;否则 a 为 p.y
var a = p?.y;

构建函数

两种构建函数:

class Point {
  double x;
  double y;

  // this.x 是语法糖
  // 构建函数的函数体为空时,花括号 {} 也可以不写
  Point(this.x, this.y);
  Point.fromJson(Map<String, double> json)
      : x = json['x'],
        y = json['y'];
}

void main() {
  var p1 = Point(1, 2);
  var p2 = Point.fromJson({'x': 1, 'y': 2});
  assert(p1.x == p2.x);
}

构建函数都是不会继承给子类的。

默认构建函数

如果你没有定义 constructor,Dart 会默认有一个 constructor,它不接受参数,不做任何事情;如果你定义了 constructor,不管它是带参数还是不带参数,默认的 constructor 都不会再生效。

如果子类没有定义 constructor,Dart 会默认有一个 constructor,它会调用父类的 no-arg constructor,再调用子类的。顺序如下:

  1. 对类中带默认值的 property 求值
  2. 对 initializer list 求值
  3. 调用父类的 no-arg constructor
  4. 调用子类的 no-arg constructor

写法例子:

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

initializer list 的 N 种写法

Point.fromJson(Map<String, double> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}
class Employee extends Person {
  Employee() : super.fromJson(defaultData);
  // ···
}
Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}

设置 final 的 pattern:

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

重定向到别的构建函数:

class Point {
  double x, y;

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

  // Delegates to the main constructor.
  Point.alongXAxis(double x) : this(x, 0);
}

Factory constructors

有些情况下,我们希望构建函数执行时:

  • 不一定要构建新的对象,比如在缓存中取已有的对象
  • 返回一个子类的对象

此时可以用 factory 关键字:

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) {
    return _cache.putIfAbsent(
        name, () => Logger._internal(name));
  }

  factory Logger.fromJson(Map<String, Object> json) {
    return Logger(json['name'].toString());
  }

  Logger._internal(this.name);

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

操作符重载

Dart 提供了一种很像 C++ 的操作符重载能力:

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  // Operator == and hashCode not shown.
  // ···
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}

Getters and setters

Getter 和 setter 是把对属性的读写变成(不带参数的)函数:

class Rectangle {
  double left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // Define two calculated properties: right and bottom.
  double get right => left + width;
  set right(double value) => left = value - width;
  double get bottom => top + height;
  set bottom(double value) => top = value - height;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

抽象类

// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
  // Define constructors, fields, methods...

  void updateChildren(); // Abstract method. (methods that has no implementation)
}

隐式接口

Dart 没有专门的接口结构(比如 Java 的 Interface)。它通过类和其属性的访问限制,隐式地定义了接口。

// A person. The implicit interface contains greet().
class Person {
  // In the interface, but visible only in this library.
  final _name;

  // Not in the interface, since this is a constructor.
  Person(this._name);

  // In the interface.
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// An implementation of the Person interface.
class Impostor implements Person {
  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')));
  print(greetBob(Impostor()));
}

mixins

mixin 是一种避免了多继承、但又可以共享一部分代码的机制:

mixin Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;

  void entertainMe() {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self');
    }
  }
}

你也可以用 class 关键词取代 mixin,但用 mixin 关键词时,Musical 是无法被实例化的。

在类声明中 with Musical 的类,可以用 mixin 中的代码,而不需要继承它:

class Musician extends Performer with Musical {
  // ···
}

class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}

另外,如果 Musical 中的 entertainMe() 是抽象函数(没有实现),with Musical 的类都需要实现这个函数。类似的,canPlayPiano 这几个值,如果没有默认值,with Musical 的类都需要给它设置 getter / setter / override(?)。

类静态方法

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

Dart 建议使用 top-level functions 而不是 static methods。

可调用的类

通过在类中实现一个 call 方法,使它的实例可以像函数一样被调用:

class WannabeFunction {
  String call(String a, String b, String c) => '$a $b $c!';
}

var wf = WannabeFunction();
var out = wf('Hi', 'there,', 'gang');

main() => print(out);