Dart基础之Classes(类)上篇

前言

Dart是一种面向对象的语言,具备类和混合继承。 每一个对象都是一个类的实例,全部类都来自Object。 混合继承意味着虽然每一个类(除了Object)只有一个超类(也称为父类),可是类体能够在多个类层次结构中重用。json

如何使用类成员

对象拥有由函数和数据(分别为方法和实例变量)组成的成员。在一个对象上调用方法时:该方法能够访问该对象的函数和数据。缓存

用逗号(.)去获取一个实例的变量或者方法:ide

var p = Point(2, 2);

// 设置实例 p 的 属性 y 为 3
p.y = 3;

// 获取实例 p 的属性 y
assert(p.y == 3);

// 调用实例 p 的 distanceTo()
num distance = p.distanceTo(Point(4, 4));
复制代码

当最左边的操做数为null时,请用?.来代替.以免异常发生函数

// 当 p 为非空时, 才会把 y 设置为 4
p?.y = 4;
复制代码

利用构造函数

你能够用构造函数来建立一个对象, 构造函数的名字能够是 ClassName 或者 ClassName.identifier。例如,下面的代码用Point()Point.fromJson()构造函数建立Point对象:ui

var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
复制代码

下面的代码有相同的做用,可是在构造函数名字前用了一个可选的关键字new:this

var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
复制代码

注意版本:关键字new在Dart2中变为可选的spa

一些类提供了常量构造函数。为了建立一个编译时的常量可使用常量构造函数,把const关键字放在构造函数名称前就能够了:code

var p = const ImmutablePoint(2, 2);
复制代码

构建两个相同的编译时常量会产生一个单个,标准的实例:对象

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // a和b是同一个实例
复制代码

在一个常量上下文中,你能够在构造函数或者文字前省略const。例如,看下下面的代码,建立了一个map常量:继承

// 茫茫多的 const 关键字
const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
复制代码

你能够省略除了第一个的其余const关键字:

// 只要一个 const 就能够创建常量上下文
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
复制代码

若是一个常量构造函数在常量上下文的外面,而且没有const调用, 它会建立一个很是量的对象:

var a = const ImmutablePoint(1, 1); // 建立一个常量
var b = ImmutablePoint(1, 1); // 没有建立一个常量

assert(!identical(a, b)); // 不是同一个实例
复制代码

注意版本:关键字 const在 Dart2 的常量上下文中才变可选

获取一个对象的类型

在运行时获取一个对象的类型,你可使用ObjectruntimeType属性,它会返回一个Type对象。

print('The type of a is ${a.runtimeType}');
复制代码

看到这里,你已经知道怎么去使用classes了。剩下的章节教你如何去实现classes

实例变量

class Point {
  num x; //申明一个实例变量x, 初始值null
  num y; //申明一个y,初始值null
  num z = 0; //申明一个z,初始值0 
}
复制代码

全部没有初始化过的实例变量,默认值都是 null

全部实例变量会生成一个隐式的的getter方法。全部的非 final 定义的实例变量会生成一个隐式的setter方法。

class Point {
  num x;
  num y;
}

void main() {
  var point = Point();
  point.x = 4; // 使用了 x 的 `setter` 方法.
  assert(point.x == 4); // 使用了 x 的 `getter` 方法.
  assert(point.y == null); // y 没有赋值的状况下默认 `getter` 方法拿到的值是 `null`.
}
复制代码

若是你在申明时初始化了一个实例变量(代替了在构造函数或者方法中),当实例被建立时就会被赋值,至关于在构造函数和它的初始化列表执行以前。

构造函数

建立一个跟类同名的函数来申明一个构造函数(增长,可选,一个额外的identifier称为命名构造函数)。下面是构造函数最普通的模式,制造型的构造函数,用来建立一个类的实例:

class Point {
  num x, y;

  Point(num x, num y) {
    // 有更好的方式实现这种赋值
    this.x = x;
    this.y = y;
  }
}
复制代码

this关键字指的是当前的实例。

注意:只有当存在一个命名冲突时使用this,另外Dart的风格是省略this

经过构造函数给实例变量赋值的方式很是广泛,因此 Dart 有语法糖让它变得更简单:

class Point {
  num x, y;

  // 在构造函数方法体中实现变量的赋值
  Point(this.x, this.y);
}
复制代码

默认构造函数

即便你不申明一个构造函数,Dart 会默认提供一个给你。默认构造函数没有参数而且会触发超类(父类)的无参构造函数。

构造函数不能被继承

子类不能继承它们超类的构造函数。一个子类申明无参构造函数只能是默认的(无参数,无命名)构造函数。

命名构造函数

用一个命名构造函数实现多个构造函数,来为一个类提供更清晰的表达:

class Point {
  num x, y;

  Point(this.x, this.y);

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

请记住,构造函数不是继承的,这意味着超类的命名构造函数不会被子类继承。 若是但愿使用超类中定义的命名构造函数建立子类,则必须在子类中实现该构造函数。

调用非默认的超类构造函数

默认状况下,子类中的构造函数调用超类的未命名的无参数构造函数。 超类的构造函数会在构造函数体的开头被调用。 若是还使用初始化列表,则在调用超类以前执行。 总之,执行顺序以下:

  • 初始化列表
  • 超类的无参数构造函数
  • 主类的无参数构造函数

若是超类没有未命名的无参数构造函数,则必须手动调用超类中的一个构造函数。 在冒号(:)以后指定超类构造函数,就在构造函数体(若是有)以前。

在下面的示例中,Employee类的构造函数为其超类Person调用命名构造函数。

class Person {
  String firstName;

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

class Employee extends Person {
  // Person 没有一个默认的构造函数
  // 你必须调用 super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

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

  // 输出:
  // in Person
  // in Employee
  if (emp is Person) {
    // Type check
    emp.firstName = 'Bob';
  }
  (emp as Person).firstName = 'Bob';
}
复制代码

由于在调用构造函数以前会计算超类构造函数的参数,因此参数能够是一个表达式,例如函数调用:

class Employee extends Person {
  Employee() : super.fromJson(getDefaultData());
  // ···
}
复制代码

警告:超类构造函数的参数无权访问它。 例如,参数能够调用静态方法,但不能调用实例方法。

初始化列表

除了调用超类构造函数以外,还能够在构造函数体运行以前初始化实例变量。 用逗号分隔初始化程序。

// 初始化列表会在构造函数体运行以前设置实例变量
Point.fromJson(Map<String, num> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}
复制代码

警告:初始化程序的右侧无权访问this

在开发期间,你可使用初始化列表中的assert来验证输入。

Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}
复制代码

初始化列表时能够很方便设置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);
}
复制代码

重定向构造函数

有时构造函数的惟一目的是重定向到同一个类中的另外一个构造函数。 重定向构造函数的主体是空的,构造函数调用出如今冒号(:)以后。

class Point {
  num x, y;

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

  // 委托给了主构造函数
  Point.alongXAxis(num x) : this(x, 0);
}
复制代码

常量构造函数

若是你的类生成对象后永远不会更改,那么可使这些对象成为编译时常量。 为此,请定义const构造函数并确保全部实例变量都是final

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

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}
复制代码

工厂构造函数

在实现并不老是建立一个类新实例的构造函数时,请使用factory关键字。 例如,工厂构造函数可能从缓存中返回实例,或者它可能返回子类型的实例。

如下示例演示了从缓存中返回对象的工厂构造函数:

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

  // _cache 是库私有的
  static final Map<String, Logger> _cache =
      <String, Logger>{};

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

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}
复制代码

注意:工厂构造函数没有权限访问this

像调用任何其余构造函数同样调用工厂构造函数:

var logger = Logger('UI');
logger.log('Button clicked');
复制代码
相关文章
相关标签/搜索