方法
关于Functions的称呼为“方法”还是“函数”,这么多年也一直是程序员喜欢没事就瞎扯的话题。在我习惯里面向对象的语言习惯性将它称为“方法”。
Dart是一个真正的面向对象的编程语言,方法在Dart里面也会被视为对象,并且Dart赋予了它一种类型——Function。
因为是一个对象的存在,所以方法可以赋值给变量。
下面是定义一个返回bool类型的方法示例:
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
官方文档还有提到可以忽略类型定义的方法,但从Java编程经验出发,不是很建议这么定义,同时Effective Dart中也推荐在公开的APIs上使用静态类型:
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
同样可以使用=> expr语法来书写一个表达式的方法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
参数
固定参数
固定参数这个是所有语言都支持的,每个参数位置对应调用时参数的值,如下示例代码所示:
showName(String firstName, String lastName) {
// ...
}
上面的方法里面有两个参数,分别为firstName和lastName,那么在调用这个方法的时候就必须同时具备这两个参数,即使空值也要传一个null。
上面的方法调用如下:
showName('Kevin','Wu');
可选参数
在Dart里面有“可选命名参数”和“可选位置参数”两种。
可选命名参数
可选命名参数使用上与OC类似,但不同的是它是可选的,不是必须的。
下面是一个可选命名参数的示例:
showName({String firstName, String lastName}) {
// ...
}
那么它调用时firstName和lastName都是可选的,以下是几种示例调用方法:
//都不指定
showName();
//只指定firstName
showName(firstName: 'Kevin');
//只指定lastName
showName(lastName: 'Wu');
//指定firstName和lastName
showName(firstName: 'Kevin', lastName: 'Wu');
showName(lastName: 'Wu', firstName: 'Kevin');
上面例子可以看出,在可选命名参数上,firstName和lastName是没有顺序要求的,但在混合固定参数的情况下还是要注意固定参数的顺序是不能改变的,如下例子所示:
showName(int gender, {String firstName, String lastName}) {
// ...
}
调用时gender是一定要写在前面的,如下所示:
//不指定firstName和lastName
showName(1);
//只指定gender和firstName
showName(1, firstName: 'Kevin');
//只指定gender和lastName
showName(1, lastName: 'Wu');
//指定gender,firstName和lastName
showName(1, firstName: 'Kevin', lastName: 'Wu');
showName(1, lastName: 'Wu', firstName: 'Kevin');
可选位置参数
可选位置参数,顾名思义,就是通过位置来确定参数的对应的。
以下是一个可选位置参数的示例:
showName([String firstName, String lastName]){
//...
}
调用时firstName和lastName都是可选的,但要注意的是,想指定lastName则必须先指定firstName,因为他是以位置来对应参数的:
//都不指定
showName();
//只指定firstName
showName('Kevin');
//只指定lastName
showName(null,'Wu');
//指定firstName和lastName
showName('Kevin', 'Wu');
至于混合固定参数的调用方式,与可选命名参数类似,只是加了位置的限制,这里就不展开说明了。
默认参数值
前面介绍基本概念时都提到,Dart一切皆为对象,所以在使用可选参数的方法中要特别注意,虽然你定义了可选的参数,你要考虑当它为null的时候的容错处理,否者直接使用可能会抛出空异常Uncaught exception: Invalid argument: null,抑或,指定默认值。
在可选命名参数中,指定默认值示例如下:
showName({String firstName, String lastName = 'A'}) {
// ...
}
在调用这个方法时,如果只指定了firstName,则这个方法的lastName的值就是'A'。
在可选位置参数中也是一样的。
main()入口方法
main()方法基本上是我们学每门语言都避不开需要去了解的,因为它往往都是一门语言里面的入口方法。
其中我们在前面文章中创建的第一个Flutter应用时自动生成代码中的main()方法如下:
void main() => runApp(MyApp());
这是个无参的main()方法,这个方法中调用了runApp()方法。
而如果在命令行应用中,main()方法还可以携带参数的,比如下面例子以一个String类型从控制台输入参数并将参数打印出来:
void main(String arguments) {
print(arguments);
}
匿名方法和一等方法对象
大部分方法都是带有方法名的,但这不是必须的,在dart里面也可以创建没有方法名的方法,称为匿名方法,也成为匿名方法,有时候也被称为lambda或者closure闭包。你可以把匿名方法赋值给一个变量,然后你可以使用这个方法,比如添加到集合或者从集合中删除。
比如需要使用集合类的forEach()方法遍历打印内容,可以这么写:
var list = [1, 2, 3];
list.forEach((element){
print(element);
});
同样也可以把方法当做参数来调用另一个方法,这个属于一等方法对象的定义:
var list = [1, 2, 3];
printElement(element) {
print(element);
}
list.forEach(printElement);
也可以将方法赋值给一个变量:
var list = [1, 2, 3];
var pFun = (e) {
print(e);
};
list.forEach(pFun);
静态作用域
Dart语言的静态作用域定义和Java的作用域类似,只需要记住一点在大括号里面定义的变量就只能在大括号方位访问。
下面是官方文档的一个示例:
var topLevel = true;
main() {
var insideMain = true;
myFunction() {
var insideFunction = true;
nestedFunction() {
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
}
}
上面代码块中,nestedFunction()方法时可以访问所有的变量的,包括顶级变量。
闭包
一个闭包是一个方法对象,不管这个对象在哪里被调用,这个对象都可以访问他作用域内的变量。
以一个例子来详细梳理下这个过程。
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
main() {
var add2 = makeAdder(2);
print(add2(4));
}
上面写法是语法糖写法,下面我们切换回常规写法看看:
Function makeAdder(num addBy) {
Function sumfun = (num i) {
return addBy + i;
};
return sumfun;
}
main() {
var add2 = makeAdder(2);
print(add2(4));
}
可以看到main()方法中定义了一个变量add2,用来接收makeAdder(2)的赋值,实际上也是返回一个方法,这个方法就是makeAdder()里面的sumfun(),而在print()中的add(4)实际上是调用了以下方法:
Function sumfun = (num i) {
return addBy + i;
};
而addBy是来自它上级的参数,这就说明了不管这个对象在哪里被调用,这个对象都可以访问他作用域内的变量。
简单理解的话就是闭包就是将一个方法返回给另一个方法。
返回值
所有的函数都返回一个值。如果没有指定返回值,则默认返回null。
本文摘抄自KevinWu的博客
网友评论