Void-7's

Dart基础:类的构造函数

flutter_logo.png

对于常见的构造函数,Dart提供了如下的语法糖:

/*常规*/
class Point {
  num x, y; //Dart中int和double是num的子类

  //this引用当前类对象
  Point(num x, num y) {
    this.x = x;
    this.y = y;
  }
}

/*语法糖*/
class Point {
  num x, y;
  //用于设置x和y的语法糖
  //在构造函数体运行之前
  Point(this.x, this.y);

  
  String toString() {
    return ("x: $x, y: $y");
  }
}
//print(new Point(100, 200)); -> 打印 x: 100, y: 200

Dart

默认构造函数:如果没有声明构造函数,会自动提供一个无参的默认构造函数,并且在调用子类构造函数之前调用它:

class Parent{
  Parent(){
    print('In Parent\'s constructor.');
  }
}

class Child extends Parent{
  Child(){
    print('In Child\'s constructor.');
  }
}

//new Child(); -> 打印 In Parent's constructor.  In Child's constructor.

Dart

构造函数无法被继承,所以如果子类没有声明构造函数,只会有一个默认无参无名的构造函数

命名式构造函数

可以为一个类声明多个带标识符命名的构造函数来满足自定义的需求:

如下面矩形类的origin()构造器和长方体类full()构造器

一个自定义类的示例:

///矩形类(父类)
class Rectangle {
  num x, y; //长宽,dart中int,double是num的子类
  ///命名式构造函数,可以声明多个
  Rectangle.origin() {
    this.x = 0;
    this.y = 0;
    print("Rectangle.origin() calls");
  }

  ///默认构造函数,带参
  Rectangle(x, y) {
    this.x = x;
    this.y = y;
    print("Rectangle($x,$y) calls");
  }

  ///覆写toString方便debug
  
  String toString() =>
      "x:$x y:$y #x.runtimeType:${x.runtimeType.toString()} #y.runtimeType:${y.runtimeType.toString()}";
}

///长方体类(子类)
class Cuboid extends Rectangle {
  num h; //高

  ///默认构造函数,带参
  Cuboid(h) : super.origin() {
    this.h = h;
    print("Cuboid($h) calls");
  }

  ///命名式构造函数
  Cuboid.full(x, y, h) : super(x, y) {
    this.h = h;
    print("Cuboid($x,$y,$h) calls");
  }

  ///覆写toString方便debug
  
  String toString() =>
      "x:$x y:$y h:$h #x.runtimeType:${x.runtimeType.toString()} #y.runtimeType:${y.runtimeType.toString()} #h.runtimeType:${h.runtimeType.toString()}";
}

void main() {
  Cuboid shit = Cuboid(3.0);
  print(shit.toString());
  Cuboid fuck = Cuboid.full(3.1, 6.3, 9);
  print(fuck.toString());
}

Dart

调用父类非默认构造函数

默认情况下,子类的构造函数会调用父类的匿名无参数构造方法,并且该调用会在子类构造函数的函数体代码执行前,如果子类构造函数还有一个 初始化列表,那么该初始化列表会在调用父类的该构造函数之前被执行,总的来说,这三者的调用顺序如下:

  1. 初始化列表
  2. 父类的无参数构造函数
  3. 当前类的构造函数
1:默认先调用父类的无参数构造函数

class Parent{
  Parent(){
    print('In Parent\'s constructor.');
  }
}


class Child extends Parent{
  Child(num x, num y) {
    print('In Child\'s constructor.');
  }
}

//new Child(100, 100); -> 打印
//In Parent's constructor.
//In Child's constructor.2:手动调用父类其他构造函数

class Parent{
  num x;
  num y;
  Parent(this.x, this.y){
    print('In Parent\'s constructor.');
  }
}

class Child extends Parent{
  Child(num x, num y) : super(x, y){
    print('In Child\'s constructor.');
  }
}

//new Child(100, 100); -> 打印
//In Parent's constructor.
//In Child's constructor.


关于初始化列表:
调用父类构造函数之前,可以在构造函数体执行之前初始化示例变量,用逗号分隔,该过程叫初始化列表。

class Parent{
  num x;
  num y;
  num sum;

  Parent(num px, num py) : x = px, y = py, sum = px + py{
    print("x: $x, y: $y, sum: $sum");
    print('In Parent\'s constructor.');
  }
}

class Child extends Parent{
  Child(num x, num y) : super(x, y){
    print('In Child\'s constructor.');
  }
}

// new Child(100, 100); -> 打印
//x: 100, y: 100, sum: 200
//In Parent's constructor.
//In Child's constructor.

Dart

重定向构造函数

有时候类中的构造函数会调用类中其它的构造函数,该重定向构造函数没有函数体,只需在函数签名后使用(:)指定需要重定向到的其它构造函数即可:

class Point {
  double x, y;

  // 该类的主构造函数。
  Point(this.x, this.y);

  // 委托实现给主构造函数。
  Point.alongXAxis(double x) : this(x, 0);
}

Dart

如果类生成的对象都是不会变的,那么可以在生成这些对象时就将其变为编译时常量。你可以在类的构造函数前加上 const 关键字并确保所有实例变量均为 final 来实现该功能。

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

  final double x, y;

  const ImmutablePoint(this.x, this.y);
}

Dart

如果你的类需要成为永远不会更改的对象,则可以使这些对象成为编译时常量。 定义const构造函数要确保所有实例变量都是final

class Point {
  final num x;
  final num y;
  static final Point2 origin = const Point2(0, 0);

  //const关键字放在构造函数名称之前,且不能有函数体
  const Point2(this.x, this.y);
}

Dart

工厂构造函数

在实现不用总是创建新的类对象的时候,可以使用factory关键字。 例如,工厂构造函数可能从缓存中返回实例,或者它可能返回子类型的实例:

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

  //工厂构造函数无法访问this,所以这里要用static
  //维护一个缓存的Map对象,
  static final Map<String, Logger> _cache =
      <String, Logger>{};
  
 //调用该构造函数的时候,会从Map中取出对象
  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);
  }
}

//调用
var logger = Logger('UI');
logger.log('Button clicked');

Dart

其中,Logger(String name)也可以如官方docs中一样使用putIfAbsent()实现:

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

Dart

putIfAbsent()的api说明如下:

V putIfAbsent (

​ K key,

​ V ifAbsent( )

)

Look up the value of key, or add a new value if it isn't there.

Returns the value associated to key, if there is one. Otherwise calls ifAbsent to get a new value, associates key to that value, and then returns the new value.

Map<String, int> scores = {'Bob': 36};
for (var key in ['Bob', 'Rohan', 'Sophena']) {
  scores.putIfAbsent(key, () => key.length);
}
scores['Bob'];      // 36
scores['Rohan'];    //  5
scores['Sophena'];  //  7