Dart 语法学习(二)

运算符

一般运算符这里就不说了,只列举一些特例

/ 和 ~/

两个都表示除,但是一个返回的的是 double 类型,一个返回的是 int 类型

1
2
print(5 / 2); //输出 2.5
print(5 ~/ 2); //输出 2

is 和 is!

判断是否是某个类型

1
2
print(5 is int); // 输出 true
print(5 is! String); // 输出 true

??=

只有在变量为 null 时才赋值

1
2
3
4
// Assign value to a
a = value;
// Assign value to b if b is null; otherwise, b stays the same
b ??= value;

条件表达式

有两个条件表达式,一个是 condition ? expr1 : expr2 ,当条件满足时,返回 expr1 的值,否则返回 expr2 的值;一个是 expr1 ?? expr2 ,当 expr1 不为 null 时,返回 expr1的值,否则返回 expr2

1
2
3
4
5
//如果 isPublic 等于 true,返回 public
var visibility = isPublic ? 'public' : 'private';

//如果 name 等于 null ,则输出 Guest
print(name ?? 'Guest')

级联表达式 ..

跟链式调用比较像

1
2
3
4
5
6
var list = [1, 2, 3];
list
..add(4)
..removeAt(1)
..add(5);
print(list); // 输出 [1, 3, 4, 5]

流程控制语句

跟其他语言也差不多,这里只讲稍微有区别的地方

For

标准的 for 这里也不说了,有一个可能有些语言没有的

1
2
3
4
var collection = [0, 1, 2];
for (var x in collection) {
print(x); // 0 1 2
}

Switch

对于符合条件的判断,如果有语句,就必须有 break 关键字,否则报错

1
2
3
4
5
6
7
8
9
10
var command = 'OPEN';
switch (command) {
case 'OPEN':
print(command)
// ERROR: Missing break

case 'CLOSED':
print(command)
break;
}

但是如有没有语句,却不会报错

1
2
3
4
5
6
7
var command = 'OPEN';
switch (command) {
case 'OPEN':
case 'CLOSED':
print(command);//输出 OPEN
break;
}

如果想同时有语句,又想继续执行,可以使用 continue 加标签的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
print(command);
continue nowClosed;
// Continues executing at the nowClosed label.

nowClosed:
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
print(command);
break;
}

每一个对象都是一个类的实例,大部分语言的类都差不多,由属性和行为构成,Dart 依然有着一些独到之处

一个简单的标准类

1
2
3
4
5
class Point {
num x; // Declare instance variable x, initially null.
num y; // Declare y, initially null.
num z = 0; // Declare z, initially 0.
}

类中未初始化的变量值都为 null,实例化

1
2
3
4
5
6
void main() {
var point = Point();
point.x = 4; // Use the setter method for x.
assert(point.x == 4); // Use the getter method for x.
assert(point.y == null); // Values default to null.
}

构造函数

声明一个构造函数,其实就是创建一个跟类同名的方法,用 this 关键字表示当前调用对象,仅当参数跟属性冲突的时候使用 this,否则 Dart 会忽略掉 this

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

Point(num x, num y) {
// There's a better way to do this, stay tuned.
this.x = x;
this.y = y;
}
}

Dart 的语法糖,让构造函数变简单的写法(下面的写法跟上面等效)

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

// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}
默认的构造函数

如果你没有定义构造函数,会默认生成一个无参的构造函数,并调用父类的无参构造函数

构造函数不能继承

子类不会从父类继承构造函数,当没有声明构造函数时,子类将只有一个无参的构造函数

命名构造函数

使用命名构造函数来让类有多个构造函数

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

Point(this.x, this.y);

// Named constructor
Point.origin() {
x = 0;
y = 0;
}
}
调用父类中没有默认构造函数的构造函数

当父类只有命名构造函数时,由于不能继承构造函数,此时需要子类手动去调用父类的命名构造函数

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
class Person {
String firstName;

Person.fromJson(Map data) {
print('in Person');
}
}

class Employee extends Person {
// Person does not have a default constructor;
// you must call super.fromJson(data).
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}

main() {
var emp = new Employee.fromJson({});

// Prints:
// in Person
// in Employee
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
(emp as Person).firstName = 'Bob';
}
初始化变量的另一种操作

除了调用父类的构造函数来初始化外,还可以用下面的方式在构造函数体执行的时候初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import 'dart:math';

class Point {
final num x;
final num y;
final num distanceFromOrigin;

// Initializer list sets instance variables before
// the constructor body runs.
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y) {
print('init: ($x, $y)');
}
}

main() {
var p = new Point(2, 3);
print(p.distanceFromOrigin);
}

注意:这种方式不能使用 this 关键字

调用别的构造函数

在同一个类中,有时候需要调用别的构造函数,使用冒号 :

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

// The main constructor for this class.
Point(this.x, this.y);

// Delegates to the main constructor.
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 关键字来修饰构造函数,实现实例化对象时,如果不需要产生新对象的情况。比如下面的例子,一个工厂构造函数,会返回缓存中存在的对象。(工厂构造函数不能访问 this 关键字)

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
class Logger {
final String name;
bool mute = false;

// _cache is library-private, thanks to
// the _ in front of its name.
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 关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}
}
Getters and Setters

可以通过 getset 来改变变量的默认取值和赋值方式,此时慎用 ++-- 避免意外事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Rectangle {
num left, top, width, height;

Rectangle(this.left, this.top, this.width, this.height);

// Define two calculated properties: right and bottom.
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);
print(rect.left);
rect.right = 12;
print(rect.left);
}
抽象类和抽象方法

面向对象基本都有抽象,直接看代码吧

1
2
3
4
5
6
7
8
9
10
11
abstract class Doer {
// Define instance variables and methods...

void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
void doSomething() {
// Provide an implementation, so the method is not abstract here...
}
}

隐式接口

每个类都有一个隐式接口,该接口包含所有的实例成员和它实现的任何接口,可以通过实现此接口的方式,复写其中的 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
// A person. The implicit interface contains greet().
class Person {
// In the interface, but visible only in this library.
final _name;

// Not in the interface, since this is a constructor.
Person(this._name);

// In the interface.
String greet(String who) => 'Hello, $who. I am $_name.';
}

// An implementation of the Person interface.
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');

void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}

继承类

使用 extends 继承父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}

class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
复写方法

子类可以复写父类的实例方法,getters 和 setters,用 @override 注解声明

1
2
3
4
5
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
可复写的操作符

可以复写以下操作符,来操作两个对象

< + ` ` []
> / ^ []=
<= ~/ & ~
>= * << ==
% >>

下面是复写了 +- 操作的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Vector {
final int x, y;

Vector(this.x, this.y);

Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

// Operator == and hashCode not shown. For details, see note below.
// ···
}

void main() {
final v = Vector(2, 3);
final w = Vector(2, 2);

print(v + w == Vector(4, 5));
print(v - w == Vector(0, 1));
}

如果复写 == 操作符的话,还需要复写 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
33
34
class Person {
final String firstName, lastName;

Person(this.firstName, this.lastName);

// Override hashCode using strategy from Effective Java,
// Chapter 11.
@override
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.
@override
bool operator ==(dynamic other) {
if (other is! Person) return false;
Person person = other;
return (person.firstName == firstName &&
person.lastName == lastName);
}
}

void main() {
var p1 = Person('Bob', 'Smith');
var p2 = Person('Bob', 'Smith');
var p3 = 'not a person';
print(p1.hashCode == p2.hashCode);
print(p1 == p2);
print(p1 != p3);
}

枚举

这个跟其他语言貌似也无差,定义

1
enum Color { red, green, blue }

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 通过 values
List<Color> colors = Color.values;
print(colors[2] == Color.blue);

// 通过 index
print(Color.red.index == 0);
print(Color.green.index == 1);
print(Color.blue.index == 2);

// 枚举用于 switch
var 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'
}

拓展类

使用 with 关键字后面跟随一个或多个拓展类的方式,可以实现类的拓展

1
2
3
4
5
6
7
8
9
10
11
class Musician extends Performer with Musical {
// ···
}

class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}

要实现扩展类,创建一个没有构造函数,没有使用 super 关键字的类

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');
}
}
}

静态变量和静态方法

使用 static 关键字来实现类级别的变量和方法,从此脱离对象,走上人生高峰

静态变量

静态变量在未使用之前不会初始化

1
2
3
4
5
6
7
8
class Queue {
static const initialCapacity = 16;
// ···
}

void main() {
print(Queue.initialCapacity == 16);
}
静态方法

也叫类方法,不能操作实例,不能使用 this 关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import 'dart:math';

class Point {
num x, y;
Point(this.x, this.y);

static num distanceBetween(Point a, Point b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
}

void main() {
var a = Point(2, 2);
var b = Point(4, 4);
var distance = Point.distanceBetween(a, b);
assert(2.8 < distance && distance < 2.9);
print(distance);
}

类告一段落了,以此记录,未完待续…