函数

Catalogue   

Dart 是一种真正面向对象的语言,所以即便函数也是对象并且类型为 Function,这意味着函数可以被赋值给变量或者作为其它函数的参数。你也可以像调用函数一样调用 Dart 类的实例。

1
2
3
4
5
6
7
8
/// bool可以省略,但最好保留,提高可读性
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}

//如果函数体内只包含一个表达式,你可以使用简写语法,称之为箭头函数
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

在Java中,要实现回调,需要实现一个接口,在Dart中,只需要传递一个回调函数即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void setListener(Function listener){
listener("Success");
}
//或者
void setListener(void listener(String result)){
listener("Success");
}

//第三种:类型定义 将返回值为voide,参数为一个String的方法定义为一个类型。
typedef void Listener(String result);
void setListener(Listener listener){
listener("Success");
}

1
2
3
4
5
6
7
8
9
10
//也可以选择忽略返回类型(不推荐)
int add(int i,int j) {
return i + j;
}


//对于只有一个表达式的方法,可以选择使用缩写语法来定义:
add(i, j) => i + j;
//在箭头 (=>) 和分号 (;) 之间只能使用一个表达式

构造函数

  • 如果你没有声明构造函数,那么 Dart 会自动生成一个无参数的构造函数并且该构造函数会调用其父类的无参数构造方法。
  • 子类不会继承父类的构造函数,如果子类没有声明构造函数,那么只会有一个默认无参数的构造函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Point {
double x = 0;
double y = 0;

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

//语法糖
Point(this.x, this.y);
}

默认构造函数

如果未声明构造函数,则会提供默认构造函数。 默认构造函数没有参数,并调用父类无参数构造函数。

默认情况下,子类中的构造函数调用父类的未命名无参数构造函数。 父类的构造函数在子类构造函数体的开头被调用。 如果还使用初始化了列表,则会在调用父类构造函数之前执行。

执行顺序如下:

  • 初始化列表
  • 父类的无参数构造函数
  • 子类的无参数构造函数
1
2
3
4
5
6
7
8
9
10
11
class Parent{
Parent(){
print('In Parent\'s constructor.');
}
}

class Child extends Parent{
Child(){
print('In Child\'s constructor.');
}
}

如果父类没有未命名的无参数构造函数,则必须手动调用父类中的一个构造函数。 在子类的构造函数体之后用冒号(:)指定父类构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
class Parent{
num x;
num y;
Parent(this.x, this.y){
print('In Parent\'s constructor.');
}
}

class Child extends Parent{
Child(num x, num y) : super(x, y){
print('In Child\'s constructor.');
}
}

命名构造函数

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 Point{
num x;
num y;

Point(this.x, this.y);

//创建一个坐标原点类
Point.origin(){
this.x = 0;
this.y = 0;
}

//创建一个坐标为(100, 100)的类
Point.coordinate100(){
this.x = 100;
this.y = 100;
}
@override
String toString() {
return ("x: $x, y: $y");
}
}

重定向构造函数

1
2
3
4
5
6
7
8
9
10

class Point {
num x, y;

//类的主构造函数
Point(this.x, this.y);

//重定向到主构造函数
Point.alongXAxis(num x) : this(x, 0);
}

初始化列表

除了调用父类构造函数之外,还可以在构造函数体执行之前初始化实例变量。每个实例变量之间使用逗号分隔。

1
2
3
4
5
6
7
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, double> json)
: x = json['x']!,
y = json['y']! {
print('In Point.fromJson(): ($x, $y)');
}

常量构造函数

  • 常量构造函数需以const关键字修饰
  • const构造函数必须用于成员变量都是final的类
  • 构建常量实例必须使用定义的常量构造函数
  • 如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例
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


class ImutablePoint {
final int x;
final int y;

//常量构造方法
const ImutablePoint(this.x, this.y);
}

main() {

var ip1 = const ImutablePoint(1, 1);
var ip2 = const ImutablePoint(1, 1);
var ip3 = const ImutablePoint(1, 2);
print(ip1.hashCode == ip2.hashCode);
print(ip1 == ip2);
print(ip1.hashCode == ip3.hashCode);
print(ip1 == ip3);


output:
true
true
false
false
}


工厂构造函数

由于下划线开头的变量和方法是私有的,可以运用此特性实现单例模式。

工厂构造函数不需要每次构建新的实例,且不会自动生成实例,而是通过代码来决定返回的实例对象;工厂构造函数类似于 static 静态成员,无法访问 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

class Singleton {
static final Singleton _singleton = Singleton.internal();

factory Singleton() => _singleton;

Singleton.internal();
}

class CommonPrivacyScreen {
final String title;
final String url;

factory CommonPrivacyScreen.privacy() {
return CommonPrivacyScreen(title: "title_privacy",url: "url_privacy");
}

factory CommonPrivacyScreen.agreement() {
return CommonPrivacyScreen(title: "title_agreement",url: "title_agreement");
}

CommonPrivacyScreen({Key key, this.title, this.url}) : super(key: key);
}

参数

函数可以有两种形式的参数:

  • 必要参数:定义在参数列表前面
  • 可选参数:定义在必要参数后面,可选参数可以是 命名的位置的

可选命名参数

把方法的参数放到 {} 中就变成可选命名参数,命名参数默认为可选参数,除非他们被特别标记为 required。

1
2
3
4
5
6
7
8
9
int add({int? i, int? j}) {
if(i == null || j == null){
return 0;
}
return i + j;
}
print(add());
print(add(i: 1));
print(add(i: 2, j: 4));

可选位置参数

把方法的参数放到 [] 中就变成可选位置参数,传值时按照参数位置顺序传递。

1
2
3
4
5
6
7
8
9
10
11
12
13
class SquareBracketsClass {
late int x;
late int y;
// 1、2为默认参数
SquareBracketsClass([int x=1, int y=2]) {
this.x = x;
this.y = y;
}
}
main() {
var squareBracketClass = SquareBracketsClass(3);
var squareBracketClass2 = SquareBracketsClass(3, 6);
}

默认参数值

Dart中没有函数重载,可以用 = 为函数的命名参数和位置参数定义默认值,默认值必须为编译时常量,没有指定默认值的情况下默认值为 null。

1
2
3
4
5
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold = false, bool hidden = false}) {...}

// bold will be true; hidden will be false.
enableFlags(bold: true);

函数作为参数传递

函数是一级对象,可以将函数作为参数传给另一个函数,也可以将函数赋值给一个变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

void printElement(int element) {
print(element);
}

var list = [1, 2, 3];

// Pass printElement as a parameter.
list.forEach(printElement);


var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');

匿名函数

可以创建一个没有名字的方法,称之为 匿名函数、 Lambda 表达式 或 Closure 闭包。你可以将匿名方法赋值给一个变量然后使用它,比如将该变量添加到集合或从中删除。

匿名方法看起来与命名方法类似,在括号之间可以定义参数,参数之间用逗号分割。

后面大括号中的内容则为函数体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
([[类型] 参数[, …]]) {
函数体;
};


([Type] param1, …) {
codeBlock;
};

var list = ['1', '2', '3', '4', '5'];
list.forEach((i) {
print(list[i]);
});

参考