一、 Functions (函数)
Dart 是一个真正的面向对象语言,函数也是一个对象并且具有其类型:Function。这意味着,方法可以赋值给变量,也可以当做其他方法的参数。您还可以像调用函数一样调用Dart类的实例。详情请参考 Callable classes。
Dart是不支持函数的重载的****,一个类中不允许存在同名的函数出现,这点与C语言一样。虽然不支持函数重载但是它支持可选参数的使用。
下面是定义方法的示例:
bool isNoble(int atomicNumber) {
return atomicNumber != null;
}
Dart中函数的返回值类型 和 参数类型都可以省略****,但是Effective Dart 中推荐最好不要省略:
isNoble(atomicNumber) {
return atomicNumber != null;
}
对于只包含一个表达式的函数,可以使用简写语法:(return必须省略)
bool isNoble(int atomicNumber) => atomicNumber != null; //return必须省略
//同样可以省略类型,下面等价
isNoble(atomicNumber) => atomicNumber != null;
这个 => expr
语法是 { return expr; }
形式的缩写。=>
形式 有时候也称之为 箭头语法( arrow syntax)。
注意: 在箭头 => 和冒号 ; 之间只能使用一个表达式(expression) ,而不能使用语句(statement)。 例如:你不能使用 if statement,但是可以 使用条件表达式 conditional expression。
方法可以有两种类型的参数:必需的和可选的。命名可选参数也可以标记为@required。
1.1、可选参数
可选参数可以是基于命名参数或者基于位置的参数,但是这两种参数不能同时当做可选参数。
如果参数列表中同时存在可选参数 和 必选参数,必选参数必需放在可选参数前面!
Optional named parameters(可选命名参数)
在定义方法的时候,使用 {param1, param2, …}
的形式来指定命名可选参数。
在调用方法的时候,你可以使用这种形式 paramName: value
来指定命名参数,而不能直接传值:
//函数定义,注意有{}
enableFlags({bool bold, bool hidden}) {
// ...
}
//函数调用
enableFlags(hidden: false,bold: true);//OK
enableFlags(bold: true);//OK
enableFlags();//OK
//enableFlags(false, true);//编译错误
{}表示可选参数,当然你也可以按需传入可选参数,但是必选参数必选要传递:
//a是必选参数, {}中的是可选参数
//void enableFlags({bool bold, bool hidden},int a) //错误定义,可选参数只能放在后面
void enableFlags(int a,{bool bold, bool hidden}) {
print(bold.toString()+" "+hidden.toString());
}
main() {
enableFlags(3,bold: true); //true null
enableFlags(3); //null null
//enableFlags(); 错误。有必选参数a存在,不能不传任何参数
}
注意,可选参数只能放参数列表的在最后。
Optional positional parameters(可选位置参数)
把一些方法的参数放到 [ ] 中就变成可选 位置参数了,可选位置参数只能根据位置选择传入的参数,而不能通过命名的方式,其他的效果与可选命名参数一致:
//[]表示可选位置参数
void enableFlags(int a,[bool hidden, bool bold]) {
print(bold.toString()+" "+hidden.toString());
}
main() {
enableFlags(3,true,true); //true true
enableFlags(3,true); //null true
}
Default parameter values(默认参数值)
在定义方法的时候,可以使用 = 来定义可选参数的默认值,只能用于可选参数(两种可选参数[ ]和{}都支持)。 默认值只能是编译时常量。 如果没有提供默认值,则默认值为 null (Dart中万物都是对象,没有基本类型之分)。
void enableFlags(int a ,{bool hidden = true, bool bold }) {
print(bold.toString()+" "+hidden.toString());
}
main() {
enableFlags(3); //null true
enableFlags(3,hidden: false); //null false
enableFlags(3,hidden: false,bold: false); //false false
}
版本问题: 旧版本代码可能需要使用一个冒号 (:) 而不是 = 来设置参数默认值。 原因在于 Dart SDK 1.21 之前的版本,命名参数只支持 :。 : 设置命名默认参数值在以后版本中将不能使用, 所以我们推荐你 使用 = 来设置默认值, 并 指定 Dart SDK 版本为 1.21 或者更高的版本。
还可以使用 list 或者 map 作为默认值。 下面的示例定义了一个方法 doStuff(), 参数类型分别为list和map,并分别为其指定了默认值,由于默认值必须是编译时常量,所以必须使用const修饰:
void doStuff(
{List<int> list = const [1, 2, 3], Map<String, String> gifts = const {
'first': 'paper',
'second': 'cotton',
'third': 'leather'
}})
{
print('list: $list');
print('gifts: $gifts');
}
main() {
doStuff();
}
1.2、main函数
每个应用都需要有个顶级的 main() 入口方法才能执行。 main() 方法的返回值为 void 并且有个可选的 List<String> 参数。
下面是一个 web 应用的 main() 方法:
void main() {
querySelector("#sample_text_id")
..text = "Click me!"
..onClick.listen(reverseText);
}
下面是一个命令行应用的 main() 方法,并且使用了方法参数作为输入参数:
// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
1.3、函数是一等公民(Functions as first-class objects)
函数可以作为参数来进行调用,例如:
printElement(element) {
print(element);
}
main() {
var list = [1, 2, 3];
list.forEach(printElement);
}
输出结果为:1、2、3。 我们可以看看forEach的函数定义,直接声明一个函数的声明作为函数的参数:
我们也可以为变量分配一个函数,例如下面:(这个例子使用一个匿名函数。下一节将详细介绍这些内容)
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
1.4、Anonymous functions(匿名函数)
你有可以创建没有函数名的函数,称之为匿名函数,有时候也被称为 lambda 或者 closure 闭包。 你可以把匿名方法赋值给一个变量, 然后你可以使用这个方法,比如添加到集合或者从集合中删除。您可以为变量分配一个匿名函数,例如,然后你可以使用这个方法,比如添加到集合或者从集合中删除。
匿名函数看起来类似于命名函数—有0个或者多个参数,在括号之间用逗号和可选类型标注分隔。后面的代码块包含函数的主体:
([[Type] param1[, …]]) {
codeBlock;
};
下面的示例定义了一个无类型参数item的匿名函数。list 中的每个元素都会调用这个函数来 打印出来,同时来计算了每个元素在 list 中的索引位置:
var list = ['apples', 'bananas', 'oranges'];
//1、匿名函数允许直接赋予给一个变量
var a = (item) {
print('${list.indexOf(item)}: $item');
};
list.forEach(a);//a指向一个匿名函数
//2、也允许直接传入一个匿名函数,与上面的两句效果是等价的
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
/*打印结果
0: apples
1: bananas
2: oranges
*/
如果函数只包含一个语句,可以使用箭头语法缩写:
var list = ['apples', 'bananas', 'oranges'];
var a = (item) => print('${list.indexOf(item)}: $item');
list.forEach(a);
//与上面两句等价
list.forEach((item) => print('${list.indexOf(item)}: $item'));
1.5、Lexical scope(静态作用域)
Lexical Scope (also called Static Scope)。Dart 是静态作用域语言,变量的作用域在写代码的时候就确定过了。 基本上大括号里面定义的变量就只能在大括号里面访问,和 Java 作用域类似。
这里有一个嵌套函数的例子,每个作用域级别上都有变量:
bool topLevel = true;
void main() {
var insideMain = true;
void myFunction() {
var insideFunction = true;
void nestedFunction() {
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
}
}
nestedFunction() 可以访问所有的变量, 包含顶级变量。
1.6、Lexical closures(词法闭包)
一个 闭包(匿名函数) 是一个函数对象,不管该对象在何处被调用, 该对象都可以访问其作用域内的变量。
在下面的示例中,makeAdder()捕获变量addBy。无论返回的函数到哪里,它都会记住addBy:
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
return (num i) => addBy + i; //匿名函数作为Function类型的对象进行返回
}
void main() {
// Create a function that adds 2.
var add2 = makeAdder(2);
// Create a function that adds 4.
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
在上面的示例中我们可以看出:普通函数,匿名函数都是一个Function类型的对象,并且可以作为返回值进行返回。
例如下面例子,add2指向的就是test函数对象,当执行add2()的时候实际上调用的是test()函数:
test(){
print("Hello World");
}
Function makeAdder(addBy) {//返回类型可以省略
return test ;
}
main() {
var add2 = makeAdder(2);
add2(); //Hello World
}
1.7、判断函数相等
下面是测试顶级方法、静态函数和实例函数 相等的示例:(下面都是执行通过的)
foo() {} // A top-level function
class A {
static void bar() {} // A static method
void baz() {} // An instance method
}
main() {
var x;
// Comparing top-level functions.
x = foo;
assert(foo == x);
// Comparing static methods.
x = A.bar;
assert(A.bar == x);
// Comparing instance methods.
var v = new A(); // Instance #1 of A
var w = new A(); // Instance #2 of A
var y = w;
x = w.baz;
// These closures refer to the same instance (#2),
// so they're equal.
assert(y.baz == x);
// These closures refer to different instances,
// so they're unequal.
assert(v.baz != w.baz);
}
上面的例子可以看出 同一个类的不同实例之间,对于同一个方法来说其对象是不相等的,既var v = A()
和 var w = A()
中 v.bar 与 w.bar 方法对象是不相等的。
1.8、Return values(返回值)
所有函数都返回一个值。如果没有指定返回值,则 默认把语句 return null,作为函数的最后一个语句执行。
foo() {}
assert(foo() == null);
二、异常
代码中可以出现异常和捕获异常。异常表示一些 未知的错误情况。如果异常没有捕获, 则异常会抛出,导致 抛出异常的代码终止执行。
和 Java 不同的是,所有的 Dart 异常是非检查异常 unchecked exceptions(既不需要一定要用try ...catch处理)。方法不声明它们可能抛出哪些异常,也不要求您捕获任何异常:
test(){//方法体也不用声明
throw new FormatException("Test one ");
}
void main() {
test();//不需要try-catch
}
Dart 提供了 Exception 和 Error 类型, 以及一些子类型。你还 可以定义自己的异常类型。但是, Dart 代码可以抛出任何非 null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。( Dart抛出的异常类型不一定要是Exception和Error类型,任何非空类型都可以)。
2.1、throw
下面是抛出一个异常的示例:
throw new FormatException('Expected at least 1 section');//FormatException是Exception的子类
你也可以抛出任意对象:
throw 'Out of llamas!';
注意:在正式使用中通常抛出的是实现了Error或Exception类型的对象;
由于抛出异常是一个表达式,所以可以在 =>
语句中使用,也可以在其他能使用表达式的地方抛出异常:
void distanceTo(Point other) => throw UnimplementedError();
2.2、Catch
与java一样,Dart中可以使用 try-catch
来捕获异常,避免异常继续传递(除非你重新抛出rethrow异常)。不过与java不同的事,使用on ExceptionType
来声明捕获具体的异常类型。on ExceptionType catch(e) 通过catch可以捕获异常对象e。如果没有指定异常类型,直接使用catch(e),则表示可以捕获任何异常类型。
只捕获OutOfLlamasException异常:
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
对于可以抛出多种类型异常的代码,你可以指定 多个捕获语句:
void main() {
try {
// throw new FormatException("Hello"); 只触发第一个on FormatException
// throw new TimeoutException("hello");只触发第二个on Exception catch (e)
throw "Hello EeveyOne"; //只触发第三个catch (e)
} on FormatException {
//捕获具体的异常类型FormatException,没有异常对象
print("I'm a OutOfLlamasException");
} on Exception catch (e) {
//只要是Exception类型的异常都能捕获,并携带异常对象e
print('Unknown exception: $e');
} catch (e) {
// 任何throw抛出的类型都能捕获
print('Something really unknown: $e');
}
}
如上面代码所示,你可以使用on 或者 catch 来声明捕获语句,也可以 同时使用。使用 on 来指定异常类型,使用 catch 来 捕获异常对象。
函数 catch() 可以带有一个或者两个参数, 第一个参数为抛出的异常对象, 第二个为堆栈信息 (一个 StackTrace 对象)。
void main() {
try {
throw "Hello EeveyOne";
} on Exception catch (e) {
print('Unknown exception: $e');
} catch (e,s) { //e为异常对象,s为堆栈信息
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
}
使用 rethrow 关键字可以 把捕获的异常给 重新抛出,当然也可以使用 throw抛出异常,不过使用rethrow更方便:
void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // Allow callers to see the exception.
// throw e;等价于上面
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
输出:
misbehave() partially handled NoSuchMethodError.
main() finished handling NoSuchMethodError.
2.3、Finally
要确保某些代码执行,不管有没有出现异常都需要执行,可以使用 一个 finally 语句来实现。<u>如果没有 catch 语句来捕获异常, 则在执行完 finally 语句后, 异常被抛出了</u>:
void main() {
try {
throw "Hello EeveyOne";
} finally{
//finally执行了,但是异常并没有被捕获,执行后立马抛出异常
print("My Name Finally");
}
print("End");//没有而被执行
}
打印如下:
finally子句在所有匹配到的catch子句之后运行:
void main() {
try {
throw "Hello EeveyOne";
} catch (e){
print("Exception is $e");
} finally{
print("My Name Finally");
}
print("End");
}
输出结果为:
Exception is Hello EeveyOne
My Name Finally
End
想了解更多请阅读[Exceptions]章节。
欢迎关注我的公众号【不喝咖啡的程序员】,最新的文章会在上面发布: