Dart第五章-Class。
Class
Dart是一个面向对象语言,同时支持基于 mixin 的继承机制。每个对象都是一个实例,所有的类都继承自 Object。 基于 Mixin 的继承意味着每个类(Object 除外) 都只有一个父类,一个类的代码可以在其他 多个类继承中重复使用。
使用 new
关键字和构造函数来创建新的对象。
1
2
3
4
5
var p = new Point();
class Point {
}
每个实例变量都会自动生成一个 getter
方法(隐含的)。 Non-final
实例变量还会自动生成一个 setter
方法。
定义实例变量
1
2
3
4
5
class Point {
num a;//null
num b;//null
num c = 0;
}
1
2
3
var p = new Point();
p.a = 100;
assert(p.a == 100);
所有没有初始化的变量值都是 null
。
每个实例变量都会自动生成一个 getter
方法(隐含的)。 Non-final
实例变量还会自动生成一个 setter 方法,参见下文Getter&Setter
。
构造函数
定义一个和类名字一样的方法就定义了一个构造函数 还可以带有其他可选的标识符。
1
2
3
4
5
6
7
8
9
10
class Point {
num a;
num b;
num c = 0;
//不推荐
Point(num a, num b) {
this.a = a;
this.b = b;
}
}
因为这种赋值操作太常见了,所以Dart提供了如下语法糖。
1
2
3
4
5
6
7
class Point {
num a;
num b;
num c = 0;
Point(this.a, this.b);
}
Default constructors(默认构造函数)
如果你没有定义构造函数,则会有个默认构造函数。 默认构造函数没有参数,并且会调用超类的没有参数的构造函数。
Constructors aren’t inherited(构造函数不会继承)
子类不会继承超类的构造函数。子类如果没有定义构造函数,则只有一个默认构造函数 (没有名字没有参数)。
Named constructors(命名构造函数)
使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明你的意图:
1
2
3
4
5
6
7
8
9
10
class Point {
num a;
num b;
num c = 0;
Point.fromJson(Map map) {
this.a = map['a'];
this.b = map['b'];
}
}
注意:构造函数不能继承,所以超类的命名构造函数 也不会被继承。如果希望 子类也有超类一样的命名构造函数,必须在子类中自己实现该构造函数。
Invoking a non-default superclass constructor(调用超类构造函数)
默认情况下,子类的构造函数会自动调用超类的 无名无参数的默认构造函数。
超类的构造函数在子类构造函数体开始执行的位置调用。 如果提供了一个 initializer list(初始化参数列表)[见下文]
,则初始化参数列表在超类构造函数执行之前执行。下面是构造函数执行顺序:
- 1,initializer list(初始化参数列表)
- 2,superclass’s no-arg constructor(超类的无名构造函数)
- 3,main class’s no-arg constructor(主类的无名构造函数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void main() {
var stu = Student.fromJson({});
}
class Person {
String name;
Person.fromJson(Map json) {
print("invoke Person constructor");
}
}
class Student extends Person {
Student.fromJson(Map json) : super.fromJson(json) {
print("invoke Student constructor");
}
}
执行顺序为:
1
2
invoke Person constructor
invoke Student constructor
由于超类构造函数的参数在构造函数执行之前执行,所以 参数可以是一个表达式或者 一个方法调用:
1
2
3
4
class Student extends Person {
// ...
Student() : super.fromJson(findDefaultData());
}
注意: 如果在构造函数的初始化列表中使用 super(),需要把它放到最后。
Initializer list(初始化列表)
在构造函数体执行之前除了可以调用超类构造函数之外,还可以初始化实例参数。 使用逗号分隔初始化表达式。
1
2
3
4
5
6
7
8
9
10
11
12
13
class Point {
num a;
num b;
num square;
Point(x, y)
: a = x,
b = y,
square = x * y;
}
var p = Point(10,10);
print(p.square);//100
Redirecting constructors(重定向构造函数)
有时候一个构造函数会调动类中的其他构造函数。 一个重定向构造函数是没有代码的,在构造函数声明后,使用冒号调用其他构造函数。
1
2
3
4
5
6
7
8
9
class Point {
num a;
num b;
num square;
Point(this.a, this.b);
Point.alongX(x) : this(x, 0);
}
Constant constructors(常量构造函数)
如果类提供一个状态不变的对象,可以把这些对象 定义为编译时常量。要实现这个功能,需要定义一个 const 构造函数, 并且声明所有类的变量为 final。
1
2
3
4
5
6
7
class ImmutablePoint {
final num x;
final num y;
const ImmutablePoint(this.x, this.y);
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
}
Factory constructors(工厂方法构造函数)
如果一个构造函数并不总是返回一个新的对象,则使用 factory 来定义 这个构造函数。例如,一个工厂构造函数 可能从缓存中获取一个实例并返回,或者 返回一个子类型的实例。
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
void main() {
var _logger = new Logger("qfxl");
}
class Logger {
String name;
Logger._internal(this.name);
static final Map<String, Logger> _cache = Map<String, Logger>();
factory Logger(name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
var logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
void log(String content) {
print(content);
}
}
Getters and setters
Getters 和 setters 是用来设置和访问对象属性的特殊 函数。每个实例变量都隐含的具有一个 getter, 如果变量不是 final 的则还有一个 setter。 你可以通过实行 getter 和 setter 来创建新的属性, 使用 get 和 set 关键字定义 getter 和 setter:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
num get right => left + width;
set right(value) => left = value - width;
num get bottom => height + top;
set bottom(value) => top = value - height;
}
getter 和 setter 的好处是,可以开始使用实例变量,后来可以把实例变量用函数包裹起来,而调用代码的地方不需要修改。
定义函数
Instance methods(实例函数)
1
2
3
4
5
6
7
8
9
10
11
12
13
class Point {
num a;
num b;
num square;
Point(this.a, this.b);
num diffSqrt(Point other) {
var diffA = a - other.a;
var diffB = b - other.b;
return diffA * diffB;
}
}
Abstract methods(抽象函数)
实例函数、 getter、和 setter 函数可以为抽象函数, 抽象函数是只定义函数接口但是没有实现的函数,由子类来 实现该函数。如果用分号来替代函数体则这个函数就是抽象函数。
1
2
3
4
5
6
7
8
9
10
abstract class Docoer {
void decor();
}
class MyDecoer extends Docoer {
@override
void decor() {
}
}
Overridable operators(可覆写的操作符)
具体可以复写的操作符如下:
< | + | | | [] |
---|---|---|---|
> | / | ^ | []= |
<= | ~/ | & | ~ |
>= | * | « | == |
- | % | » |
如:
1
2
3
4
5
6
7
8
9
10
class MyPoint {
num a;
num b;
MyPoint(this.a, this.b);
MyPoint operator +(MyPoint point) {
return new MyPoint(a + point.a, b + point.b);
}
}
如果覆写了 ==
,则还应该覆写对象的 hashCode getter
函数。
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
26
27
28
29
30
31
32
class Person {
final String firstName, lastName;
Person(this.firstName, this.lastName);
// Override hashCode using strategy from Effective Java,
// Chapter 11.
int get hashCode {
int result = 17;
result = 37 * result + firstName.hashCode;
result = 37 * result + lastName.hashCode;
return result;
}
// You should generally implement operator == if you
// override hashCode.
bool operator ==(other) {
if (other is! Person) return false;
Person person = other;
return (person.firstName == firstName &&
person.lastName == lastName);
}
}
main() {
var p1 = new Person('bob', 'smith');
var p2 = new Person('bob', 'smith');
var p3 = 'not a person';
assert(p1.hashCode == p2.hashCode);
assert(p1 == p2);
assert(p1 != p3);
}
Abstract classes(抽象类)
使用 abstract 修饰符定义一个 抽象类—一个不能被实例化的类。 抽象类通常用来定义接口,以及部分实现。如果希望你的抽象类 是可示例化的,则定义一个工厂构造函数。
抽象类通常具有抽象函数。
1
2
3
4
5
6
7
8
abstract class Docoer {
void decor();
}
class MyDecoer extends Docoer {
@override
void decor() {}
}
Interfaces(接口)
每个类都隐式的定义了一个包含所有实例成员的接口, 并且这个类实现了这个接口。如果你想 创建类 A 来支持 类 B 的 api,而不想继承 B 的实现, 则类 A 应该实现 B 的接口。 一个类可以通过 implements 关键字来实现一个或者多个接口, 并实现每个接口定义的 API。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class People {
final _name;
People(this._name);
String greet(who) => "hello, $who, i am $_name";
}
class Qfxl implements People {
final _name = "";// 这句必须要有
@override
String greet(who) {
return 'Hi $who. Do you know who I am?';;
}
}
继承
1
2
3
4
5
6
7
8
9
10
11
12
13
class Human {
void breath(){
}
}
class Man extends Human {
@override
void breath() {
super.breath();
}
}
枚举
1
2
3
4
5
enum Color {
red,
green,
blue
}
枚举类型中的每个值都有一个 index
getter 函数, 该函数返回该值在枚举类型定义中的位置(从 0 开始)。 例如,第一个枚举值的位置为 0, 第二个为 1。
1
2
var r = Color.red;
print(r.index); //0
枚举的 values
常量可以返回 所有的枚举值。
1
2
List<Color> colors = Color.values;
print(colors);
输出:
1
[Color.red, Color.green, Color.blue]
可以在 switch 语句 中使用枚举。 如果在 switch (e)
中的 e 的类型为枚举类, 如果没有处理所有该枚举类型的值的话,则会抛出一个警告。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enum Color {
red,
green,
blue
}
// ...
Color aColor = Color.blue;
switch (aColor) {
case Color.red:
print('Red as roses!');
break;
case Color.green:
print('Green as grass!');
break;
default: // Without this, you see a WARNING.
print(aColor); // 'Color.blue'
}
拓展类(为类添加新功能)
Mixins
是一种在多类继承中重用一个类代码的方法。
使用 with
关键字后面为一个或者多个 mixin
名字来使用 mixin
。
定义一个类继承 Object
,该类没有构造函数,不能调用 super ,则该类就是一个 mixin
。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
abstract class 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');
}
}
}
拓展类
1
2
3
class Musician extends Performer with Musical {
// ...
}
静态变量与函数
静态变量
1
2
3
4
5
6
7
8
9
10
void main() {
assert(Color.red.name == "red");
}
class Color {
static const red = const Color("red");
final String name;
const Color(this.name);
}
静态变量在第一次使用的时候才被初始化。
静态函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() {
Color.printColor();//输出qfxl
}
class Color {
static const red = const Color("red");
final String name;
const Color(this.name);
static void printColor() {
print("qfxl");
}
}
对于通用的或者经常使用的静态函数,考虑 使用顶级方法而不是静态函数。
静态函数还可以当做编译时常量使用。例如,可以把静态函数当做常量构造函数的参数来使用。