Classes
Dart 是一种面向对象的语言,拥有类和基于 mixin 的继承。每个对象都是一个类的实例,并且除了 Null
之外的所有类都派生自 Object
。基于 mixin 的继承意味着尽管每个类(除了顶级类 Object?
)都只有一个超类,但一个类的代码体可以在多个类层次结构中被复用。扩展方法是一种在不修改类或创建子类的情况下为类添加功能的方式。类修饰符允许你控制库如何对一个类进行子类型化。
使用类成员
对象拥有由函数和数据组成的成员(分别为方法和实例变量)。当你调用一个方法时,你是在一个对象上调用 (invoke) 它:该方法可以访问该对象的函数和数据。
使用点号 (.
) 来引用一个实例变量或方法:
1 | var p = Point(2, 2); |
当最左边的操作数可能为 null 时,使用 ?.
代替 .
来避免产生异常:
1 | // 如果 p 不为 null,则将其 y 值赋给一个变量。 |
使用构造函数
你可以使用构造函数 (constructor) 来创建一个对象。构造函数的名称可以是 ClassName
或 ClassName.identifier
。例如,下面的代码使用 Point()
和 Point.fromJson()
构造函数来创建 Point
对象:
1 | var p1 = Point(2, 2); |
下面的代码效果相同,但在构造函数名前使用了可选的 new
关键字:
1 | var p1 = new Point(2, 2); |
有些类提供常量构造函数 (constant constructors)。要使用常量构造函数创建一个编译时常量,请在构造函数名前加上 const
关键字:
1 | var p = const ImmutablePoint(2, 2); |
构造两个相同的编译时常量会产生一个单一的、规范的实例:
1 | var a = const ImmutablePoint(1, 1); |
在常量上下文中,你可以省略构造函数或字面量之前的 const
。例如,看这段创建了一个 const
map 的代码:
1 | // 这里有很多 const 关键字。 |
你可以省略掉除了第一个 const
关键字之外的所有 const
:
1 | // 只有一个 const,它建立了常量上下文。 |
如果一个常量构造函数在常量上下文之外且未使用 const
调用,它会创建一个非常量对象:
1 | var a = const ImmutablePoint(1, 1); // 创建一个常量 |
获取对象的类型
要在运行时获取一个对象的类型,你可以使用 Object
的 runtimeType
属性,它会返回一个 Type
对象。
1 | print('The type of a is ${a.runtimeType}'); |
警告
使用类型测试操作符而不是runtimeType
来测试对象的类型。在生产环境中,测试object is Type
比测试object.runtimeType == Type
更稳定。
到目前为止,你已经了解了如何使用类。本节的其余部分将展示如何实现类。
实例变量
下面是你如何声明实例变量的方式:
1 | class Point { |
一个使用可空类型声明但未初始化的实例变量,其值为 null
。不可空的实例变量必须在声明时初始化。
所有实例变量都会生成一个隐式的 getter 方法。非 final
的实例变量和没有初始化器的 late final
实例变量还会生成一个隐式的 setter 方法。详情请查阅 Getters and setters。
1 | class Point { |
在声明处初始化一个非 late
的实例变量,会在实例创建时、在构造函数及其初始化列表执行之前设置该值。因此,一个非 late
实例变量的初始化表达式(=
之后的部分)不能访问 this
。
1 | double initialX = 1.5; |
实例变量可以是 final
的,这种情况下它们必须且只能被设置一次。请在声明时、使用构造函数参数、或使用构造函数的初始化列表来初始化 final
、非 late
的实例变量:
1 | class ProfileMark { |
如果你需要在构造函数体开始之后才为一个 final
实例变量赋值,你可以使用以下方法之一:
- 使用一个工厂构造函数。
- 使用
late final
,但要小心:一个没有初始化器的late final
变量会向 API 中添加一个 setter。
隐式接口
每个类都隐式地定义了一个接口,该接口包含了该类的所有实例成员以及它所实现的任何接口的成员。如果你想创建一个支持类 B 的 API 但不继承 B 的实现的类 A,那么类 A 应该实现 (implement) B 接口。
一个类通过在 implements
子句中声明一个或多个接口,然后提供这些接口所需的 API,来实现这些接口。例如:
1 | // 一个 Person。隐式接口包含了 greet()。 |
下面是一个指定一个类实现多个接口的例子:
1 | class Point implements Comparable, Location { |
类变量和方法
使用 static
关键字来实现类级别的变量和方法。
静态变量
静态变量(类变量)对于类级别的状态和常量很有用:
1 | class Queue { |
静态变量在使用前不会被初始化。
注意
本页遵循风格指南的建议,对常量名优先使用小驼峰命名法 (lowerCamelCase)。
静态方法
静态方法(类方法)不操作于实例上,因此无法访问 this
。然而,它们可以访问静态变量。如下例所示,你可以直接在类上调用静态方法:
1 | import 'dart:math'; |
注意
对于通用或广泛使用的工具和功能,考虑使用顶级函数,而不是静态方法。
你可以将静态方法用作编译时常量。例如,你可以将一个静态方法作为参数传递给一个常量构造函数。