编程语言虽然有千差万别,但是归根结底,他们的设计思想无非就是回答两个问题:
之前的文章中,我们已经了解了Dart这门语言的基本语法,也就了解了Dart是如何表示信息的了。今天就来聊聊Dart是如何处理信息的。
作为一门真正面向对象的编程语言,Dart将处理信息的过程抽象成了对象,以结构化的方式将功能分解,而函数、类与运算符就是抽象中最重要的手段。
函数
函数是一段独立完成某功能的代码。在Dart中,所有类型都是对象类型,函数也是对象,它的类型叫做Function。这意味着函数也可以被定义为变量,甚至可以被定义为参数传递给另一个函数。
在下面这段代码中,我定义了一个判断整数是否为0 的isZero函数,并把它传递给了printInfo函数,完成格式化打印判断结果的功能:
void main(){
printInfo(6, isZero);//输出 6 is not Zero
printInfo(0, isZero);//输出 0 is Zero
}
String isZero (int a){
return a==0 ? 'is Zero' : 'is not Zero';
}
printInfo(int a, Function check){
print('$a ${check(a)}');
}
有时,一个函数中可能需要传递多个参数。那么,如何让这类函数的参数声明变得更加优雅、可维护,同时减低调用者的使用成本呢?
C++与Java的做法是,提供函数的重载,即提供同名但是参数不同的函数。但Dart认为重载会导致混乱,因此从设计之初就不支持重载,而是提供了可选参数和可选命名参数。
具体方式是,在声明函数时:
这两者的使用以及区别,我在Dart学习笔记(三)中做了详细说明,这里不赘述。
类
类是特定类型的数据和方法的集合,也是创建对象的模板。
Dart是面向对象的语言,每一个对象都是一个类的实例,都继承自顶层类型Object。
值得一提的是,Dart中并没有public、protected、private这些关键字,我们在声明变量与方法时,在前面加上"_"即可作为private方法使用。如果不加"_",则默认为public。不过,"_"的限制范围并不是类访问级别的,而是库访问级别的。
接下来看下面一段代码:
void main(){
var p = Point(66, 88);
p.printInfo();
Point.factor = 100;
Point.printZValue();
}
class Point {
num x, y;
static num factor = 0;
Point(this.x, this.y);
printInfo() => print('($x, $y)');
static printZValue() => print(factor);
}
我在Point类中,定义了两个成员变量x和y,通过构造函数进行初始化,成员函数printInfo的作用是打印它们的信息;而类变量factor则在声明时就已经赋好了默认值0,类函数printZValue会打印出它的信息。
有的时候,类的实例化需要根据参数提供多种初始化方式。除了可选命名参数和可选参数之外,Dart还提供了命名构造函数的方式,使得类的实例化过程语义更清晰。
下面再看一段代码:
void main(){
var p = Point.bottom(66);
p.printInfo();//输出 (66, 0, 0)
}
class Point {
num x, y, z;
Point(this.x, this.y) : z=0;//初始化变量z
Point.bottom(num x) : this(x, 0);//重定向构造函数
printInfo() => print('($x, $y, $z)');
}
如代码所示,Point类中有两个构造函数Point与Point.bottom,其中Point.bottom将其成员变量的初始化重定向到了Point中,而Point则在初始化列表中为赋上了默认值0。
运算符
在Dart中,一切都是对象,就连运算符也是对象成员函数的一部分。
对于系统的运算符,一般情况下只支持基本数据类型和标准库中提供的类型。而对于用户自定义的类,如果想支持基本操作,比如比较大小、相加相减等,则需要用户自己来定义这个运算符的具体实现。
Dart提供了运算符覆写机制,使得我们不仅可以覆写方法,还可以覆写或者自定义运算符。
接下来我们一起来看一个Vector类中自定义"+"运算符和覆写"=="运算符的例子:
void main(){
final x = Vector(3, 3);
final y = Vector(2, 1);
final z = Vector(1, 2);
print(z==x+y);//false
print(x==y+z);//true
}
class Vector {
num x, y;
Vector(this.x, this.y);
//自定义相加运算符,实现向量相加
Vector operator+ (Vector v) => Vector(x+v.x, y+v.y);
//覆写相等运算符,判断向量相等
//需要注意的一点是,这里之所以要用dynamic而不是Vector,是因为operator==继承自Object类,该类的参数声明就是dynamic
bool operator== (dynamic v) => x == v.x && y == v.y;
}
operator是Dart的关键字,与运算符一起使用,表示一个类成员运算符函数。我们可以这样理解:将operator和运算符作为一个整体,看作是一个成员函数名。
总结
函数,类和运算符是Dart处理信息的抽象手段。
函数是对象,可以被定义为变量,或者参数。Dart不支持函数重载,但是提供了可选参数和可选命名参数的方式,从而解决了函数声明时需要传递多个参数的可维护性。
类提供了数据和函数的抽象复用能力,可以通过继承(父类继承extents,接口实现implement)和非继承(Mixin, with)的方式实现复用。
以上。