0%

Dart入门三

1.类

Dart 是一种基于类和 mixin 继承机制的面向对象的语言。 每个对象都是一个类的实例,所有的类都继承于 Object 。 基于 Mixin 继承 意味着每个类(除 Object 外) 都只有一个超类, 一个类中的代码可以在其他多个继承类中重复使用。类的定义和Java、TypeScript类似,定义类用class关键字

1
2
3
4
5
6
7
8
9
class Point {
num x;
num y;
}

void main() {
var point = Point(); // Dart中省略 new 关键字
point.x = 4; // Use the setter method for x.
}

构造函数

当通过类创建一个对象时,会调用这个类的构造方法。通过创建一个与其类同名的函数来声明构造函数。

在没有声明构造函数的情况下, Dart 会提供一个默认的构造函数。 默认构造函数没有参数并会调用父类的无参构造函数。

1
2
3
4
5
6
7
8
9
class Point {
num x, y;

Point(num x, num y) {
// 还有更好的方式来实现下面代码
this.x = x;
this.y = y;
}
}

Dart提供了一种更加简洁的语法糖形式

1
2
3
4
5
6
7
class 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
11
class 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
4
class 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
9
class 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
8
class 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
24
class 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
22
class Television {
String brand;

void turnOn() {
print("Television turnOn");
}

}

class SmartTelevision extends Television {
@override
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
10
class 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
15
mixin 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
2
3
mixin MusicalPerformer on Musician {
// ···
}
赞赏是最好的支持