1.类
Dart 是一种基于类和 mixin 继承机制的面向对象的语言。 每个对象都是一个类的实例,所有的类都继承于 Object
。 基于 Mixin 继承 意味着每个类(除 Object 外) 都只有一个超类, 一个类中的代码可以在其他多个继承类中重复使用。类的定义和Java、TypeScript类似,定义类用class关键字
。1
2
3
4
5
6
7
8
9class Point {
num x;
num y;
}
void main() {
var point = Point(); // Dart中省略 new 关键字
point.x = 4; // Use the setter method for x.
}
构造函数
当通过类创建一个对象时,会调用这个类的构造方法。通过创建一个与其类同名的函数来声明构造函数。
在没有声明构造函数的情况下, Dart 会提供一个默认的构造函数。 默认构造函数没有参数并会调用父类的无参构造函数。
1 | class Point { |
Dart提供了一种更加简洁的语法糖形式
。1
2
3
4
5
6
7class Point {
num x, y;
// 在构造函数体执行前,
// 语法糖已经设置了变量 x 和 y。
Point(this.x, this.y);
}
提示: 近当存在命名冲突时,使用 this 关键字。 否则,按照 Dart 风格应该省略 this 。
命名构造函数
由于Dart本身不支持函数的重载
,如果我们希望实现更多的构造方法,使用命名构造函数。1
2
3
4
5
6
7
8
9
10
11class Point {
num x, y;
Point(this.x, this.y);
// 命名构造函数
Point.origin() {
x = 0;
y = 0;
}
}
切记,构造函数不能够被继承, 这意味着父类的命名构造函数不会被子类继承。 如果希望使用父类中定义的命名构造函数创建子类, 就必须在子类中实现该构造函数。
默认情况下,子类的构造函数会自动调用父类的默认构造函数(匿名,无参数)。 父类的构造函数在子类构造函数体开始执行的位置被调用。如果提供了一个 initializer list (初始化参数列表), 则初始化参数列表在父类构造函数执行之前执行。 总之,执行顺序如下:
1 initializer list (初始化参数列表)
2 superclass’s no-arg constructor (父类的无名构造函数)
3 main class’s no-arg constructor (主类的无名构造函数)
如果父类中没有匿名无参的构造函数, 则需要手工调用父类的其他构造函数。 在当前构造函数冒号 (:
) 之后,函数体之前,声明调用父类构造函数。1
2
3
4
5 // you must call super.fromJson(data).
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
由于父类的构造函数参数在构造函数执行之前执行, 所以参数可以是一个表达式或者一个方法调用:1
2
3
4class Employee extends Person {
Employee() : super.fromJson(getDefaultData());
// ···
}
警告: 调用父类构造函数的参数无法访问 this 。 例如,参数可以为静态函数但是不能是实例函数。
初始化列表
除了调用超类构造函数之外, 还可以在构造函数体执行之前初始化实例变量。 各参数的初始化用逗号分隔。1
2
3
4
5
6
7// 在构造函数体执行之前,
// 通过初始列表设置实例变量。
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
重定向构造函数
有时构造函数的唯一目的是重定向到同一个类中的另一个构造函数。 重定向构造函数的函数体为空, 构造函数的调用在冒号 (:) 之后。1
2
3
4
5
6
7
8
9class Point {
num x, y;
// 类的主构造函数。(注意:是在冒号后面使用this调用)
Point(this.x, this.y);
// 指向主构造函数
Point.alongXAxis(num x) : this(x, 0);
}
常量构造函数
在某些情况下,传入相同值时,我们希望返回同一个对象。 为此,需要定义一个 const 构造函数, 并且声明所有实例变量为 final。1
2
3
4
5
6
7
8class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
工厂构造函数
当执行构造函数并不总是创建这个类的一个新实例时,则使用 factory
关键字。 例如,一个工厂构造函数可能会返回一个 cache 中的实例, 或者可能返回一个子类的实例。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24class Logger {
final String name;
bool mute = false;
// 从命名的 _ 可以知,
// _cache 是私有属性。
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。工厂构造方法需要返回类的实例对象。
2.类的继承
和Java一样 Dart 中的继承使用extends
关键字,子类中使用super
来访问父类。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class Television {
String brand;
void turnOn() {
print("Television turnOn");
}
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn(); //super来访问父类
print("$brand SmartTelevision turn on");
}
}
main(List<String> args) {
var tv = SmartTelevision();
tv.brand = "huawei";
tv.turnOn();
}
3.抽象类
使用 abstract
修饰符来定义 抽象类 — 抽象类不能实例化。抽象类通常用来定义接口,以及部分实现。如果希望抽象类能够被实例化,那么可以通过定义一个 工厂构造函数
来实现。
抽象类通常具有 抽象方法
。 下面是一个声明具有抽象方法的抽象类示例:1
2
3
4
5
6// 这个类被定义为抽象类 所以不能被实例化
abstract class AbstractContainer {
// 定义构造行数,字段,方法...
void updateChildren(); // 抽象方法
}
提示:抽象方法,必须存在于抽象类中。与
Java
不同的是定义抽象方法不需要abstract关键字
4.隐式接口
Dart中的接口比较特殊, 没有一个专门的关键字来声明接口。
默认情况下,每个类都隐式的定义了一个接口。如果要创建一个A类,A要支持B类的 API ,但是不需要继承B的实现, 那么可以通过A实现B的接口(因为Dart不支持多继承)。
一个类可以通过 implements
关键字来实现一个或者多个接口, 并实现每个接口要求的 API。 例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// person 类。 隐式接口里面包含了 greet() 方法声明。
class Person {
// 包含在接口里,但只在当前库中可见。
final _name;
// 不包含在接口里,因为这是一个构造函数。
Person(this._name);
// 包含在接口里。
String greet(String who) => 'Hello, $who. I am $_name.';
}
// person 接口的实现。
class Impostor implements Person {
get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
String greetBob(Person person) => person.greet('Bob');
main(List<String> args) {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
下面示例演示一个类如何实现多个接口。1
class Point implements Comparable, Location {...}
在开发中,我们通常将用于给别人实现的类声明为抽象类
5.为类添加功能:Mixin
Mixin 是复用类代码的一种途径,和vuejs
中组件的可复用功能类似,复用的类可以在不同层级,之间可以不存在继承关系。
Dart通过 with
后面跟一个或多个混入的名称,来 使用 Mixin ,下面的示例演示了两个使用 Mixin 的类:1
2
3
4
5
6
7
8
9
10class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
通过创建一个继承自 Object 且没有构造函数的类,来 实现 一个 Mixin 。 如果 Mixin 不希望作为常规类被使用,使用关键字 mixin
替换 class
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
- Mixin 使用 on 来指定可以使用 Mixin 的父类类型:
1 | mixin MusicalPerformer on Musician { |