目录

Dart 是一种面向对象的语言,具有类和基于 mixin 的继承。每个对象都是一个类的实例,除 Null 外的所有类都继承自 Object基于 mixin 的继承意味着,尽管每个类(顶级类 top classObject? 除外)只有一个超类,但可以在多个类层次结构中重用类体。 扩展方法 是一种无需更改类或创建子类即可向类添加功能的方法。 类修饰符 允许您控制库如何对类进行子类型化。

使用类成员

#

对象具有由函数和数据(分别为 方法实例变量)组成的 成员。调用方法时,您会在对象上 调用 它:该方法可以访问该对象的函数和数据。

使用点 (.) 来引用实例变量或方法:

dart
var p = Point(2, 2);

// 获取 y 的值。
assert(p.y == 2);

// 在 p 上调用 distanceTo()。
double distance = p.distanceTo(Point(4, 4));

当最左边的操作数为 null 时,使用 ?. 代替 . 以避免异常:

dart
// 如果 p 不为 null,则将一个变量设置为其 y 值。
var a = p?.y;

使用构造函数

#

您可以使用 构造函数 创建对象。构造函数名称可以是 ClassNameClassName.identifier。例如,以下代码使用 Point()Point.fromJson() 构造函数创建 Point 对象:

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

以下代码具有相同的效果,但在构造函数名称之前使用了可选的 new 关键字:

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

某些类提供 常量构造函数 。要使用常量构造函数创建编译时常量,请在构造函数名称前加上 const 关键字:

dart
var p = const ImmutablePoint(2, 2);

构造两个相同的编译时常量会导致单个规范实例:

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

assert(identical(a, b)); // 它们是同一个实例!

常量上下文 中,您可以省略构造函数或文字之前的 const 。例如,请查看此代码,该代码创建了一个 const 映射:

dart
// 此处有很多 const 关键字。
const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};

您可以省略除第一个 const 关键字之外的所有关键字:

dart
// 只有一个 const,它建立了常量上下文。
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};

如果常量构造函数位于常量上下文之外,并且在没有 const 的情况下被调用,则它会创建一个 非常量对象

dart
var a = const ImmutablePoint(1, 1); // 创建一个常量
var b = ImmutablePoint(1, 1); // 不创建常量

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

获取对象的类型

#

要在运行时获取对象的类型,您可以使用 Object 属性 runtimeType ,它返回一个 Type 对象。

dart
print('a 的类型是 ${a.runtimeType}');

到这里,您已经了解了如何 使用 类。本节的其余部分将介绍如何 实现 类。

实例变量

#

以下是声明实例变量的方法:

dart
class Point {
  double? x; // 声明实例变量 x,初始值为 null。
  double? y; // 声明 y,初始值为 null。
  double z = 0; // 声明 z,初始值为 0。
}

使用 可空类型 声明的未初始化实例变量的值为 null 。不可空实例变量 必须在声明时初始化

所有实例变量都会生成一个隐式 getter 方法。没有初始值的非 final 实例变量和 late final 实例变量还会生成一个隐式 setter 方法。有关详细信息,请查看 Getter 和 Setter

dart
class Point {
  double? x; // 声明实例变量 x,初始值为 null。
  double? y; // 声明 y,初始值为 null。
}

void main() {
  var point = Point();
  point.x = 4; // 使用 x 的 setter 方法。
  assert(point.x == 4); // 使用 x 的 getter 方法。
  assert(point.y == null); // 值默认为 null。
}

在声明处初始化非 late 实例变量会在创建实例时设置值,在构造函数及其初始化列表执行之前。因此,非 late 实例变量的初始化表达式(在 = 之后)不能访问 this

dart
double initialX = 1.5;

class Point {
  // 确定,可以访问不依赖于 `this` 的声明:
  double? x = initialX;

  // 错误,不能在非 `late` 初始化程序中访问 `this` :
  double? y = this.x;

  // 确定,可以在 `late` 初始化程序中访问 `this` :
  late double? z = this.x;

  // 确定, `this.x` 和 `this.y` 是参数声明,而不是表达式:
  Point(this.x, this.y);
}

实例变量可以是 final ,在这种情况下,必须精确设置一次。在声明时初始化 final 、非 late 实例变量,使用构造函数参数,或使用构造函数的 初始化列表

dart
class ProfileMark {
  final String name;
  final DateTime start = DateTime.now();

  ProfileMark(this.name);
  ProfileMark.unnamed() : name = '';
}

如果您需要在构造函数体开始后分配 final 实例变量的值,可以使用以下方法之一:

隐式接口

#

每个类都隐式定义一个接口,其中包含该类的所有实例成员以及它实现的任何接口的所有实例成员。如果您想创建一个支持类 B 的 API 但不继承 B 的实现的类 A,则类 A 应该实现 B 接口。

一个类通过在 implements 子句中声明它们,然后提供接口所需 API 来实现一个或多个接口。例如:

dart
// 一个人的。隐式接口包含 greet()。
class Person {
  // 在接口中,但仅在此库中可见。
  final String _name;

  // 不在接口中,因为这是一个构造函数。
  Person(this._name);

  // 在接口中。
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// Person 接口的实现。
class Impostor implements Person {
  String 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()));
}

以下是如何指定类实现多个接口的示例:

dart
class Point implements Comparable, Location {...}

类变量和方法

#

使用 static 关键字实现类变量和方法。

静态变量

#

静态变量(类变量)对于类范围的状态和常量很有用:

dart
class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

静态变量直到使用时才会初始化。

静态方法

#

静态方法(类方法)不作用于实例,因此无法访问 this 。但是,它们可以访问静态变量。如下例所示,您可以直接在类上调用静态方法:

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

您可以将静态方法用作编译时常量。例如,您可以将静态方法作为参数传递给常量构造函数。