类
Dart 是一种面向对象的语言,支持类和基于混入(mixin)的继承。每个对象都是类的实例,除了 Null
之外的所有类都继承自 Object
。基于混入的继承意味着虽然每个类(除了顶层类 Object?
)都只有一个超类,但类体可以在多个类层次结构中复用。扩展方法是一种在不改变类或创建子类的情况下向类添加功能的方式。类修饰符允许你控制库如何子类型化一个类。
使用类成员
#对象拥有由函数和数据组成的成员(分别为方法和实例变量)。当你调用一个方法时,你是在对象上调用它:该方法可以访问该对象的函数和数据。
使用点 (.
) 来引用实例变量或方法
var p = Point(2, 2);
// Get the value of y.
assert(p.y == 2);
// Invoke distanceTo() on p.
double distance = p.distanceTo(Point(4, 4));
使用 ?.
而不是 .
来避免左侧操作数为 null 时发生异常
// If p is non-null, set a variable equal to its y value.
var a = p?.y;
使用构造函数
#你可以使用构造函数创建对象。构造函数名称可以是 ClassName
或 ClassName.identifier
。例如,以下代码使用 Point()
和 Point.fromJson()
构造函数创建 Point
对象
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
以下代码具有相同的效果,但使用可选的 new
关键字在构造函数名称前
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
某些类提供常量构造函数。要使用常量构造函数创建编译时常量,请将 const
关键字放在构造函数名称前
var p = const ImmutablePoint(2, 2);
构造两个相同的编译时常量会得到一个单一的规范实例
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // They are the same instance!
在常量上下文中,你可以省略构造函数或字面量前的 const
。例如,请看这段创建常量映射的代码
// Lots of const keywords here.
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
你可以省略除第一次使用 const
关键字以外的所有情况
// Only one const, which establishes the constant context.
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
如果一个常量构造函数在常量上下文之外且没有使用 const
调用,它会创建一个非常量对象
var a = const ImmutablePoint(1, 1); // Creates a constant
var b = ImmutablePoint(1, 1); // Does NOT create a constant
assert(!identical(a, b)); // NOT the same instance!
获取对象的类型
#要在运行时获取对象的类型,你可以使用 Object
属性 runtimeType
,它会返回一个 Type
对象。
print('The type of a is ${a.runtimeType}');
至此,你已经了解了如何使用类。本节的其余部分将展示如何实现类。
实例变量
#以下是如何声明实例变量
class Point {
double? x; // Declare instance variable x, initially null.
double? y; // Declare y, initially null.
double z = 0; // Declare z, initially 0.
}
使用可空类型声明的未初始化实例变量的值为 null
。非空实例变量必须在声明时初始化。
所有实例变量都会生成一个隐式的getter方法。非 final 实例变量和没有初始化器的 late final
实例变量也会生成一个隐式的setter方法。有关详细信息,请参阅Getter 和 Setter。
class Point {
double? x; // Declare instance variable x, initially null.
double? y; // Declare y, initially null.
}
void main() {
var point = Point();
point.x = 4; // Use the setter method for x.
assert(point.x == 4); // Use the getter method for x.
assert(point.y == null); // Values default to null.
}
在声明处初始化非 late
实例变量会在实例创建时设置其值,这发生在构造函数及其初始化列表执行之前。因此,非 late
实例变量的初始化表达式(在 =
之后)不能访问 this
。
double initialX = 1.5;
class Point {
// OK, can access declarations that do not depend on `this`:
double? x = initialX;
// ERROR, can't access `this` in non-`late` initializer:
double? y = this.x;
// OK, can access `this` in `late` initializer:
late double? z = this.x;
// OK, `this.x` and `this.y` are parameter declarations, not expressions:
Point(this.x, this.y);
}
实例变量可以是 final
类型,在这种情况下它们必须且只能被设置一次。初始化 final
、非 late
实例变量可以在声明时进行,也可以通过构造函数参数或使用构造函数的初始化列表进行
class ProfileMark {
final String name;
final DateTime start = DateTime.now();
ProfileMark(this.name);
ProfileMark.unnamed() : name = '';
}
如果你需要在构造函数体开始后为 final
实例变量赋值,可以使用以下方法之一
隐式接口
#每个类都隐式定义了一个接口,其中包含该类的所有实例成员以及它实现的任何接口的成员。如果你想创建一个类 A,它支持类 B 的 API 而不继承 B 的实现,那么类 A 应该实现 B 接口。
一个类通过在 implements
子句中声明一个或多个接口,然后提供接口所需的 API 来实现它们。例如
// A person. The implicit interface contains greet().
class Person {
// In the interface, but visible only in this library.
final String _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 {
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()));
}
以下是指定一个类实现多个接口的示例
class Point implements Comparable, Location {
...
}
类变量与方法
#使用 static
关键字来实现类范围的变量和方法。
静态变量
#静态变量(类变量)对于类范围的状态和常量非常有用
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
assert(Queue.initialCapacity == 16);
}
静态变量在使用前不会被初始化。
静态方法
#静态方法(类方法)不作用于实例,因此无法访问 this
。但是,它们可以访问静态变量。如下例所示,你可以直接在类上调用静态方法
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);
}
你可以将静态方法用作编译时常量。例如,你可以将静态方法作为参数传递给常量构造函数。