Dart 是一种面向对象的语言,具有类和基于 mixin 的继承。每个对象都是一个类的实例,所有的类都是 Object 的子类。
基于 mixin 的继承意味着,尽管每个类(除了 Object )都只有一个超类,但类主体可以在多个类层次结构中重用。
无论数字、函数和 null 都是对象。
new 关键字可选。尽量不用。
构造函数
class Point {
num x, y;
Point(num x, num y) {
this.x = x;
this.y = y;
}
}
等价的更简单的写法
class Point {
num x, y;
// 没有函数体直接分号结束
Point(this.x, this.y);
}
如果不声明构造函数,则提供默认构造函数。默认构造函数没有参数,并在超类中调用无参数构造函数。
子类不从父类继承构造函数。没有声明构造函数的子类只有默认的构造函数(没有参数,没有名称)而不是从父类继承的构造函数。Java 是必须继承父类构造函数。
命名的构造函数
使用命名构造函数可以在一个类中定义多个构造函数,或者让一个类的作用对于开发人员来说更清晰:
class Point {
num x, y;
// 一个构造函数
Point(this.x, this.y);
// 一个命名的构造函数
Point.origin() {
x = 0;
y = 0;
}
}
调用非默认的超类构造函数
默认情况下,子类中的构造函数调用父类的未命名的无参数构造函数。父类的构造函数在构造函数体的开始处被调用。如果类中有使用初始化列表,初始化列表将在调用超类之前执行。
如果超类没有未命名的无参数构造函数,则必须手动调用超类中的一个构造函数。在冒号 :
之后,在构造函数体(如果有的话)之前指定超类构造函数。
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 = Employee.fromJson({});
// 输出
// in Person
// in Employee
if (emp is Person) { // 类型检查
emp.firstName = 'Bob';
}
(emp as Person).firstName = 'Bob';
}
初始化列表
除了调用超类构造函数之外,还可以在构造函数主体运行之前初始化实例变量。初始值设定项用逗号分开。
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 = Point(2, 3);
print(p.distanceFromOrigin); // 3.605551275463989
}
重定向构造函数
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;
// 缓存对象
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name]; // 缓存里有就取出缓存
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
工厂构造函数不能访问 this 关键字。
调用工厂构造函数,就像调用其他构造函数一样:
var logger = Logger('UI');
logger.log('Button clicked');
方法
实例方法
实例方法和 Java 一样。
import 'dart:math';
class Point {
num x, y;
Point(this.x, this.y);
num distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}
getter/setter
所有实例变量都生成隐式 getter 方法。非最终实例变量也生成隐式 setter 方法。
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个属性,right 和 bottom,它们的 getter 和 setter 需要计算,不能用默认的了
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}