對于公有 API,最好提供類型注解。
類型注解是非常重要的文檔,它說明了相應的庫應當如何使用。為參數(shù)以及公有方法的返回類型注解有利于使用者了解 API 需要什么參數(shù)以及它能提供什么功能。
但是,如果有個 API 可以接收任何參數(shù),或者是 Dart 中無法表示的值,那么該 APi 可以不用添加注解。
對于庫內部的代碼(即便是私有的,或者是嵌套的函數(shù)),請再你認為有幫助的地方添加注解,但是不要認為你必須提供這些注解。
install(id, destPath) { // bad
// ...
}
在上面的代碼中,我們就不清楚 id
到底是什么。字符串?那么 destPath
又是什么呢?字符串還是文件對象?這個函數(shù)是同步的還是異步的?
Future<bool> install(PackageId id, String destPath) { // good
// ...
}
當你加上類型之后,這個函數(shù)的相關信息也就說明白了。
對于局部變量,最好是使用 var
而不是類型注解來聲明。
現(xiàn)在,我們更加傾向于讓函數(shù)體的代碼盡可能的簡潔,并且局部變量的類型在初始化表達式中是可以推測出來的,在這種情況下顯示聲明其類型是沒有必要的。好點的編輯器可以推測出局部變量的類型,所以自動補全功能仍然是可用的,并且你所期望的其他功能也可以正常使用。
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;
}
如果對代碼的運行性能有要求,那么在傳遞參數(shù)的時候,類型注解最好是用 double
或者 int
來代替 num
。
單一調用點(有著穩(wěn)定輸入類型)在優(yōu)化時要比多態(tài)調用點(其輸入類型可能會發(fā)生變化)更加容易。
對于數(shù)字類型,在添加類型注解時你應該指明具體的數(shù)字類型。明確地聲明 double
或者 int
有助于使用你方法的人為之傳遞相應的參數(shù)。
在正式初始化的時候不要做類型注解。
如果一個構造函數(shù)的參數(shù)使用了 this.
來初始化字段,那么該參數(shù)的類型必然和這個字段相同,故而不必使用類型注解。
class Point { // good
int x, y;
Point(this.x, this.y);
}
class Point { // bad
int x, y;
Point(int this.x, int this.y);
}
你應該避免在函數(shù)表達式中使用類型注解。
函數(shù)表達式的一大優(yōu)點就是它的簡潔性。如果一個函數(shù)復雜到需要注解類型來理解它,它就應該是一個函數(shù)語句或者是一個方法。相應的,如果一個函數(shù)簡潔到可以作為一個表達式,那么它就不需要類型注解。
var names = people.map((person) => person.name); // good
var names = people.map((Person person) { // bad
return person.name;
});
應當避免在不需要的時候使用 dynamic
來添加類型注解。
在 Dart 中,多數(shù)情況下類型注解都是可以忽略的,因為它們會被自動當做 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
來表明參數(shù)是對象。
有些操作可能對于任何對象都適用。比如,toString()
用于輸出信息,并且可以用于任何對象。在 Dart 中有兩種類型可以匹配所有對象:Object
以及 dynamic
。但是,它們表示的是兩種不同的東西。
Object
注解表明 “我可以接收任何對象,只要它含有相關對象自身定義的方法?!?/p>
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.');
}
更多建議: