首页 > 技术文章 > 【dart 学习】 构造函数

mamamia 2020-09-16 15:50 原文

dart 构造函数

ClassName(...) //普通构造函数

普通构造函数
这就是我们普通的构造函数,其样子和其它语言几乎一样

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的赋值变得简单一些,就不用写构造函数的方法体了,记得括号后用分号哦。


Classname.identifier(...) //命名构造函数

 命名构造函数

class Point {
  num x, y;

  Point(this.x, this.y);

  // 命名构造函数,新增代码
  Point.origin() {
    x = 0;
    y = 0;
  }
}

请记住,命名构造函数不可继承,如果子类想要有 和父类一样的命名构造函数,那就写个同名的(通常也会在子类的命名构造函数里,调用父类的同名命名构造函数)


const ClassName(...) //常量构造函数

常量构造函数总结如下几点:

常量构造函数需以const关键字修饰
const构造函数必须用于成员变量都是final的类
构建常量实例必须使用定义的常量构造函数
如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例
下面结合实例,对以上几点加以说明

 

正确的常量构造函数定义
根据以上的总结,定义一个Point类,包含一个常量构造函数,注意其成员变量都是final类型,且构造函数用const修饰

class Point {
  final int x;
  final int y;
  const Point(this.x, this.y);
}
常量构造函数需以const关键字修饰
如下代码定义一个const对象,但是调用的构造方法不是const修饰的,则会报The constructor being called isn't a const constructor.错误

void main() {
  const point = Point(1, 2); // 报错
}
 
class Point {
  final int x;
  final int y;
  Point(this.x, this.y);
}
const构造函数必须用于成员变量都是final的类
如下代码中成员变量x为非final,会报Can't define a const constructor for a class with non-final fields.错误

class Point {
  int x;
  final int y;
  const Point(this.x, this.y);
}
 

构建常量实例必须使用定义的常量构造函数
如下代码,定义一个const对象,但是调用的却是非常量构造函数,会报The constructor being called isn't a const constructor.错误

void main() {
  var point = const Point(1, 2); // 报错
  print(point.toString());
}
 
class Point {
  int x;
  int y;
  Point(this.x, this.y); // 非const构造函数
  
  String toString() {
    return 'Point(${x}, ${y})';
  }
}
 

如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例
如下代码,用常量构造函数构造一个对象,但是未用const修饰,那么该对象就不是const常量,其值可以再改变

void main() {
  var point = Point(1, 2); // 调用常量构造函数,但是未定义成常量
  print(point.toString());
  point = Point(10, 20); // 可以重新赋值,说明定义的变量为非常量
  print(point.toString());
}
 
class Point {
  final int x;
  final int y;
  const Point(this.x, this.y);
  
  String toString() {
    return 'Point(${x}, ${y})';
  }
}
 

factroy ClassName(...) //工厂构造函数

工厂构造函数的定义
工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象.

工厂构造函数的关键字为factory,下面我们用工厂构造函数写一个只能创造一个实例的类.

class A{
  String name;

  static A cache;

  factory A([String name='A']){
    if(A.cache===null){
      A.cache=new A.newObject(name);
    }
    return A.cache;
  }

  A.newObject(this.name);
}

void main(){
  A a=new A('HelloWorld');
  print(a.name);

  A b=new A('HelloDart');
  print(b.name);

  print(a===b);
}
A类具有一个名为cache的静态成员,它将缓存A类的第一个实例.

在工厂构造函数A中,首先判断A.cache是否已经存在实例(即判断是否是第一次实例化),如果存在则返回缓存的实例,不存在则新建一个实例并缓存.

A.newObject是一个命名构造函数,用于创建实例.

运行这段代码,会输出

HelloWorld
HelloWorld
true
其中字符串”HelloWorld”是第一个实例的name成员,在声明变量b时改用了”HelloDart”,但返回的实例的name成员仍然是”HelloWorld”.

再用===运算符判断一下它们是否属于同一个对象,结果为true,变量a和b指向的是同一个对象.

3.8.2 工厂构造函数的特性
工厂构造函数类似于static静态成员,无法访问this指针,所以在工厂构造函数中能处理的任务较为有限.

使用工厂构造函数时往往需要定义一个命名构造函数用来生产实例.

注意点

初始化列表
初始化列表的执行顺序,在整个构造函数的最前面,它除了可以调用父类的构造函数,还可以在构造函数方法体之前,初始化一些成员变量。

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

参考:
链接:https://www.jianshu.com/p/a0ba5f2ac5ce

 

推荐阅读