类型注解
对于公有 API,最好提供类型注解。
类型注解是非常重要的文档,它说明了相应的库应当如何使用。为参数以及公有方法的返回类型注解有利于使用者了解 API 需要什么参数以及它能提供什么功能。
但是,如果有个 API 可以接收任何参数,或者是 Dart 中无法表示的值,那么该 APi 可以不用添加注解。
对于库内部的代码(即便是私有的,或者是嵌套的函数),请再你认为有帮助的地方添加注解,但是不要认为你必须提供这些注解。
install(id, destPath) { // bad
// ...
}
在上面的代码中,我们就不清楚 id
到底是什么。字符串?那么 destPath
又是什么呢?字符串还是文件对象?这个函数是同步的还是异步的?
Future<bool> install(PackageId id, String destPath) { // good
// ...
}
当你加上类型之后,这个函数的相关信息也就说明白了。
对于局部变量,最好是使用 var
而不是类型注解来声明。
现在,我们更加倾向于让函数体的代码尽可能的简洁,并且局部变量的类型在初始化表达式中是可以推测出来的,在这种情况下显示声明其类型是没有必要的。好点的编辑器可以推测出局部变量的类型,所以自动补全功能仍然是可用的,并且你所期望的其他功能也可以正常使用。
Map<int, List<Person>> groupByZip(Iterable<Person> people) { // good
var peopleByZip = new Map<int, List<Person>>();
for (var person in people) {
peopleByZip.putIfAbsent(person.zip, () => <Person>[]);
peopleByZip[person.zip].add(person);
}
return peopleByZip;
}
Map<int, List<Person>> groupByZip(Iterable<Person> people) { // bad
Map<int, List<Person>> peopleByZip = new Map<int, List<Person>>();
for (Person person in people) {
peopleByZip.putIfAbsent(person.zip, () => <Person>[]);
peopleByZip[person.zip].add(person);
}
return peopleByZip;
}
如果对代码的运行性能有要求,那么在传递参数的时候,类型注解最好是用 double
或者 int
来代替 num
。
单一调用点(有着稳定输入类型)在优化时要比多态调用点(其输入类型可能会发生变化)更加容易。
对于数字类型,在添加类型注解时你应该指明具体的数字类型。明确地声明 double
或者 int
有助于使用你方法的人为之传递相应的参数。
在正式初始化的时候不要做类型注解。
如果一个构造函数的参数使用了 this.
来初始化字段,那么该参数的类型必然和这个字段相同,故而不必使用类型注解。
class Point { // good
int x, y;
Point(this.x, this.y);
}
class Point { // bad
int x, y;
Point(int this.x, int this.y);
}
你应该避免在函数表达式中使用类型注解。
函数表达式的一大优点就是它的简洁性。如果一个函数复杂到需要注解类型来理解它,它就应该是一个函数语句或者是一个方法。相应的,如果一个函数简洁到可以作为一个表达式,那么它就不需要类型注解。
var names = people.map((person) => person.name); // good
var names = people.map((Person person) { // bad
return person.name;
});
应当避免在不需要的时候使用 dynamic
来添加类型注解。
在 Dart 中,多数情况下类型注解都是可以忽略的,因为它们会被自动当做 dynamic
类型。所以,不添加类型注解在语义上是完全没问题的,并且代码会显得更简洁。
lookUpOrDefault(String name, Map map, defaultValue) { // good
var value = map[name];
if (value != null) return value;
return defaultValue;
}
dynamic lookUpOrDefault(String name, Map map, dynamic defaultValue) { // bad
var value = map[name];
if (value != null) return value;
return defaultValue;
}
对于接收的任何对象,应该使用 Object
代替 dynamic
来表明参数是对象。
有些操作可能对于任何对象都适用。比如,toString()
用于输出信息,并且可以用于任何对象。在 Dart 中有两种类型可以匹配所有对象:Object
以及 dynamic
。但是,它们表示的是两种不同的东西。
Object
注解表明 “我可以接收任何对象,只要它含有相关对象自身定义的方法。”
dynamic
类型注解表明没有注解可以说明你实际允许的对象是什么类型。(也可能有,但是你不在意是否要声明)
//good
// Accepts any object.
void log(Object object) {
print(object.toString());
}
// Only accepts bool or String, which can't be expressed in a type annotation.
bool convertToBool(arg) {
if (arg is bool) return arg;
if (arg is String) return arg == 'true';
throw new ArgumentError('Cannot convert $arg to a bool.');
}