Dart 的单行注释也是 //
,注释内容最好是个完整的句子,一般以大写字母开头,以句点或其余标点符号结束:html
// Not if there is nothing before it.
if (_chunks.isEmpty) return false;
复制代码
/* ... */
只适合用来注掉某一段暂时不用的代码,不适合当成注释使用。
///
是 Dart 推荐使用的文档注释语法,dartdoc 会根据文档注释生成 HTML 文档,使用方括号能够连接到一个类、方法、字段等位置:git
/// Feeds your llama [Food].
///
/// The typical llama eats one bale of hay per week.
void feed(Food food) {
// ...
}
复制代码
因为历史缘由, Dart 也支持 JavaDoc 样式的文档注释 /** ... */
,但这种类型的文档注释会产生两个无实际内容的行,对于使用 *
标志列表项等场景并不方便,因此推荐使用 ///
文档注释。github
能够赋值给变量的都是对象,包括数字、函数和 null
对象,对象都是类的实例,都继承自 Object
。
变量存的是引用。
虽然 Dart 是强类型语言,可是类型声明是可选的,由于 Dart 能够推断出变量到底是什么类型的。
可使用 var
关键字声明变量而无需指定具体类型,如 var name = 'Bob';
,var number = 42;
。
有些操做须要处理任何可能的对象,如 log()
方法须要调用给定对象的 toString()
。而在 Dart 中有两种类型: Object
和 dynamic
,Object
适合表示能够是任何对象的场景,而 dynamic
适合表示更复杂的类型,如意味着 Dart 的类型系统已经不足以表示的一系列容许的类型,或者值来自 interop 或 其余超过静态类型系统范围的类型,或者你想明确地声明运行时动态处理的变量:express
void log(Object object) {
print(object.toString());
}
/// Returns a Boolean representation for [arg], which must
/// be a String or bool.
bool convertToBool(dynamic arg) {
if (arg is bool) return arg;
if (arg is String) return arg == 'true';
throw new ArgumentError('Cannot convert $arg to a bool.');
}
复制代码
若是变量永远不会改变,可使用 final
或 const
去声明,final 变量只能赋值一次,const 变量是编译时常量(也是隐式 final
的),final 的顶级类变量是在其第一次被使用时初始化的。实例变量能够是 final
的,但不能是 const
的。
const
不单能声明常量,还能够建立常量值,或者声明能够建立常量值的构造器。编程
num
类型有两个子类型 int
和 double
,int
型位数不超过64位,在 Dart VM 上是 -263 到 263 - 1,double
是64位浮点数,遵循 IEEE 754 标准。
String
类型是 UTF-16 编码,可使用单引号也可使用双引号,所以能够灵活的避免字符串限定符。能够利用 ${expression}
在字符串中使用表达式的值,若是表达式是个标识符,那么 {}
能够省略。字符串拼接可使用紧邻的字符串或者 +
号:api
var s1 = 'String '
'concatenation'
" works even over line breaks.";
var s2 = 'The + operator ' + 'works, as well.';
复制代码
多行的字符串可使用三个单引号或三个双引号包裹。 可使用前缀 r
建立原始字符串:缓存
// 在原始字符串中,即便是 \n 也再也不是特殊字符了
var s = r"In a raw string, even \n isn't special.";
复制代码
Dart 的集合 API 包含 lists, sets, 和 maps。List
可使用传统构造器建立,也可使用 []
语法建立,Map
可使用传统构造器建立,也可使用 {}
语法建立:bash
// Use a List constructor.
var vegetables = new List();
// Or simply use a list literal.
var fruits = ['apples', 'oranges'];
// Maps can be built from a constructor.
var searchTerms = new Map();
// Maps often use strings as keys.
var hawaiianBeaches = {
'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'],
'Big Island': ['Wailea Bay', 'Pololu Beach'],
'Kauai': ['Hanalei', 'Poipu']
};
复制代码
List
和 Set
都实现了 Iterable
,能够经过迭代器去遍历,而 Map
能够经过它的keys
和 values
属性获取迭代器。markdown
Dart 是真正的面向对象语言,因此即便是函数也是对象,类型是 Function
。函数做为一类对象(first-class objects),能够做为参数传递给其余函数。
若是函数体只包含一个表达式语句,可使用 =>
简写,如:闭包
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
复制代码
能够简写成:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
复制代码
这种箭头一般称做胖箭头(fat arrow)。
区分 expression 和 statement 有时仍是挺重要的,不包含 if
等控制流语句的程序元素均可以称之为表达式,包括操做符表达式,return
子句,throw
子句,print
等函数的调用。
可选参数有两种,命名可选参数和有序可选参数,可选参数必须放在全部必须参数的后面。命名可选参数的列表使用 {param1, param2, …}
声明,使用 paramName: value
调用。有序可选参数的列表使用 [param1, param2, …]
声明:
void enableFlags(String styleName, {bool bold, bool hidden}) {
}
...
enableFlags("NORMAL", hidden: false);
复制代码
String say(String from, String msg, [String device]) {
}
...
say('Bob', 'Howdy');
say('Bob', 'Howdy', 'smoke signal');
}
复制代码
可选参数能够经过 =
指定默认参数值,这个默认参数值必须是编译时常量,若是不指定默认参数值,那么默认参数值为 null
,因此不要显示地设置默认值为 null
:
void enableFlags({bool bold = false, bool hidden = false}) {
}
...
enableFlags(bold: true);
复制代码
每一个 app 都必须有一个顶层 main()
函数做为 app 的入口, main()
函数的参数列表是可选的 List<String>
,返回值是 void
。
没有名字的函数有时会称为 匿名函数(anonymous function)、lambda 表达式(lambda) 或 闭包(closure),匿名函数的声明与普通函数相似:
([[Type] param1[, …]]) {
codeBlock;
};
复制代码
如:
var functionList = [];
functionList.add((int element) {
print("element:" + element.toString());
});
复制代码
Dart 是一个 lexically scoped 语言,即 词法做用域/静态做用域 语言。也就是说,变量的做用域是根据它所处代码的布局位置静态决定的,你能够根据大括号去判断变量的做用域。
闭包(closure)就是一个函数对象能够访问它词法做用域内的变量,即便这个函数在原始范围外被使用。例如,下面的例子,makeAdder()
会捕获 addBy
. 不管他返回的函数在哪,他都会记住 addBy
:
/// 返回一个匿名函数,匿名函数的返回值是
/// 匿名函数的参数加上 [addBy].
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
void main() {
// 建立一个返回值是参数值加2的函数
var add2 = makeAdder(2);
// 建立一个返回值是参数值加4的函数
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
复制代码
全部函数都有返回值,若是不指定返回值,默认会隐式添加 return null;
到函数体中。
5 / 2 的值是浮点数 2.5,而5 ~/ 2 的值才是整型的 2。
判断对象类型用 is
或 is!
操做符,使用 as
操做符进行类型转换:
if (emp is Person) {
emp.firstName = 'Bob';
}
// 能够简写成下面的语句,可是若是emp 不是 Person 类型的
// 那么下面的语句会抛出异常
(emp as Person).firstName = 'Bob';
复制代码
使用 ??=
操做符能够给一个值为 null
的变量赋值。使用 ?.
能够避免左边表达式的值为空致使的异常,如 foo?.bar
在 foo
不为空时返回 bar
属性,在 foo
为空时返回 null
。
级联符号 ..
可让你连续操做相同的对象,不单能够连续地调用函数,还能够连续地访问方法,这样作能够避免建立临时变量,从而写出更流畅的代码,流式编程更符合现代编程习惯和编程风格:
final addressBook = (new AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (new PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
复制代码
严格意义上说,级联符号不是操做符,而是 Dart 语法的一部分。
若是循环(迭代)的对象是 Iterable
对象,能够利用它的 forEach()
方法对集合中的元素进行操做,forEach()
方法的参数是一个函数,它会按序应用集合中的每一个元素给这个函数:
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
list.forEach(printElement);
复制代码
像 List
和 Set
这样的 Iterable 类还支持 for-in
循环:
var collection = [0, 1, 2];
for (var x in collection) {
print(x); // 0 1 2
}
复制代码
抛出的异常能够是任意对象,但最好是 Error
或 Exception
对象:
throw new FormatException('Expected at least 1 section');
...
throw 'Out of llamas!';
复制代码
捕获异常可使用 on
或 catch
或者同时使用:
try {
breedMoreLlamas();
} on OutOfLlamasException {
// 捕获 OutOfLlamasException 类型的异常,不须要使用异常对象
buyMoreLlamas();
} on Exception catch (e) {
// 捕获 Exception 异常,打印异常对象
print('Unknown exception: $e');
} catch (e) {
// 捕获并处理任何类型的 thrown 对象
print('Something really unknown: $e');
}
复制代码
catch()
能够有两个参数,第一个是抛出的异常对象,第二个是堆栈跟踪对象。若是捕获异常并处理后还容许该异常继续传播,可使用 rethrow
关键字:
final foo = '';
void misbehave() {
try {
foo = "You can't change a final variable's value.";
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // Allow callers to see the exception.
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
复制代码
finally
子句会在 catch
子句执行完执行。若是没有 catch
子句,那么会在finally
子句运行完才开始传播异常。
Dart 的继承是基于 mixin 的继承,也就是说,虽然每一个类(除了 Object
类)有且只有一个超类,但类体仍然能够在多个类层级上重用。
构造器的名字能够是 ClassName
(无名构造器),也能够是 ClassName.identifier
(命名构造器),实例化时的 new
关键字能够省略。
全部的实例变量都会自动生成隐式的 getter 方法,非 final 的实例变量也会自动生成隐式的 setter 方法,使用 get
和 set
关键词能够建立额外的属性和其 getter/setter 方法的实现。
Dart 有个语法糖能够方便地将构造器参数赋值给实例变量(在构造器体执行以前):
class Point {
num x, y;
Point(this.x, this.y);
}
复制代码
若是不声明构造器,会隐式包含一个无名无参构造器,并调用父类的无名无参构造器。
子类不能继承父类的构造器,因此,若是若是子类想利用父类的命名构造器建立实例,就必须实现父类的命名构造器。若是父类没有无名无参构造器,那么你就必须手动调用父类的一个构造器:
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
复制代码
初始化的顺序为: 初始化列表 → 父类无参构造器 → 主类无参构造器
能够在构造器函数体执行前初始化实例变量,初始化列表使用逗号隔开,初始化列表特别适合初始化 final 字段:
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
复制代码
重定向到同类其余构造器:
class Point {
num x, y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(num x) : this(x, 0);
}
复制代码
若是类的对象永远不会改变,就可让这个对象成为编译时常量,只须要定义 const
构造器并确保全部的实例变量是 final
的:
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
复制代码
可使用 factory
关键词声明工厂构造器,工厂构造器不必定要建立一个新的实例,能够直接返回缓存的实例,也能够返回子类型的实例,如:
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to
// the _ in front of its name.
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
复制代码
可使用 abstract
修饰符声明一个不能直接实例化的抽象类,抽象类能够包含抽象方法,抽象方法没有方法体,以分号结尾。
每一个类都隐式地定义了一个接口,该接口包含该类的全部实例成员及其实现的全部接口。能够经过 implements
子句实现一或多个接口,以逗号隔开。
使用 extends
建立子类,使用 super
引用父类,使用 @override
注解有意地重载成员。
在面向对象的语言中,多继承是个很是糟糕的设计,由于多继承会产生一系列问题,如:
多继承会使类之间的关系更加复杂。若是一个类有多个超类,超类又可能有多个超类,那么类之间的关系将会变得尤其复杂,你很难理清一个类的超类到底有哪些,继承关系又是怎样的。
多继承可能会出现菱形继承的状况,这种状况下会出现二义性,虽然 C++ 使用虚继承的方式能够暂时避免这种二义性,但会使程序可读性和可调试性变差。
因此现代的面向对象语言都不会容许多继承这种状况存在,其实继承的目的无非是为了重用并拓展类的功能,而经过继承来重用类体并非一个好的方式,首先继承会“白箱复用”,会将超类细节暴露给子类,无论子类愿不肯意。若是超类更改,其全部子类也不得不改变。并且继承在运行时的行为不易改变。
因此尽量少用继承,多用组合。继承是 is-a 的关系,而组合是 has-a 的关系。
Mixin 的概念源于 LISP 社区,在那里是指被设计用来与各个超类一块儿工做的类。对 Mixin 介绍比较详细的是 Mixins in Strongtalk 这篇论文。
一个类会隐式地定义一个 mixin, mixin 被类体定义,与类和它的超类构成函数关系,类实际上就是 mixin 的应用,是将其隐式定义的 mixin 应用于其超类的结果。Mixin应用 与 函数应用 相似,从数学上来讲,一个 mixin能够当作是超类到子类的函数,也就是说,将
做用于超类
,结果是新的
子类,在研究资料里一般写做
。所以对于 mixin 的组合来讲:
。 一个类隐式定义的 mixin 一般只会在给定超类定义处应用一次,因此为了让 mixin 可以应用不一样的超类,咱们须要独立于任何特定超类来声明 mixin,或者将类隐式定义的 mixin 剥离出来并在原始声明以外重用它。
Mixin 能够经过普通类定义的方式隐式定义,但要想提取 mixin 就必需要保证类不能声明构造器,这也避免了因为在继承链上传递构造器参数引发的各类并发症。
abstract class Collection<E> {
Collection<E> newInstance();
Collection<E> map((f) {
var result = newInstance();
forEach((E e) { result.add(f(e)); });
return result;
});
void forEach(void f(E element)) {
// ...
}
void add(E element) {
// ...
}
}
abstract class DOMElementList<E> = DOMList with Collection<E>; abstract class DOMElementSet<E> = DOMSet with Collection<E>; 复制代码
在这里,Collection
就是一个隐式声明了 mixin 的普通类,它没有构造器。而 DOMElementList
和 DOMElementSet
类就是 mixin 的应用,它们被特殊的类声明格式定义,也就是经过 with
子句声明 mixin 到 父类的应用。这两个类也是抽象的,由于他们没有实现 Collection
类的抽象方法 newInstance()
。
也就是说,DOMElementList
就是 Collection mixin ▷ DOMList,而 DOMElementSet
就是 Collection mixin ▷ DOMSet。
这样的好处是,Collection
类的代码能够在多个类层次上共享。咱们这里列举了两个类层次,一个是以 DOMList
为根的,一个是以 DOMSet
为根的。咱们不须要复制粘贴Collection
类的代码,而 Collection
的任何更改都会传播到这两个类层次中,这样也极大地解放了代码的维护。
上面的例子只是简单说明了一种形式的 mixin 应用,应用的结果是一个有名字的类。而另外一种形式,with
子句后跟上以逗号分隔的标识符列表,全部的标识符必须表示一个类,这种形式下,多个 mixin 会被组合并应用到 extends
子句的超类中,从而产生一个匿名超类,即:
class DOMElementList<E> extends DOMList with Collection<E> {
DOMElementList<E> newInstance() => new DOMElementList<E>();
}
class DOMElementSet<E> extends DOMSet with Collection<E> {
DOMElementSet<E> newInstance() => new DOMElementSet<E>();
}
复制代码
这里的 DOMElementList
再也不是 Collection mixin ▷ DOMList 的应用,而是一个新类,它的超类是这个应用。
若是 DOMList
有非平凡构造器,那么构造将会是这样:
class DOMElementList<E> extends DOMList with Collection<E> {
DOMElementList<E> newInstance() => new DOMElementList<E>(0);
DOMElementList(size): super(size);
}
复制代码
每一个 mixin 都有属于本身的被独立调用的构造器,超类也是如此。因为 mixin 的构造器不能被声明,对它的调用能够用 ;
语法省略,在底层实现时,这个调用会被放在初始化列表的开始处。
为了让多个 mixin 应用到一个类是而又不引入多个中间声明,可使用这种方便的语法糖:
class Person {
String name;
Person(this.name);
}
class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(name):super(name);
}
复制代码
在这里,Maestro
的超类就是这样的 mixin 应用:
Demented mixin ▷ Aggressive mixin ▷ Musical mixin ▷ Person
mixin 与普通继承同样,隐私性取决于其声明的位置。mixin 的应用的静态元素一样不能被继承使用,Dart 中静态元素是不能被继承的。
那 mixin 应用后的实例类型究竟是什么类型呢?毫无疑问,它必须是命名 mixin 类的子类型,也必须是原来类的子类型。原来的类有它本身的超类,为了确保指定的 mixin 应用 兼容原来的类,Dart 会给 with
子句的类额外的要求。
若是类 A 定义时的 with
子句是 mixin M,而 M 又是继承的 类 K,那么类 A 就必须支持类 K 的接口。
class S {
twice(int x) => 2 * x;
}
abstract class I {
twice(x);
}
abstract class J {
thrice(x);
}
class K extends S implements I, J {
int thrice(x) => 3* x;
}
class B {
twice(x) => x + x;
}
class A = B with K; 复制代码
A 必须支持 K 的超类 S 的隐式接口,以确保 A 确实是 M 的子类型。K 必须实现 twice()
以知足 I 的须要,同时必须实现 thrice()
以知足 J 的须要,显然 K 已经知足了这些须要。
如今咱们声明了 A,咱们从 K 的 mixin 中 拿到了 thrice()
的实现,但这个 mixin 没有提供 twice()
的实现,幸运的是,B 有这个实现,因此总的来讲,A 确实知足了 I,J 和 S 的须要。
相反,对于这样的类 D:
class D {
double(x) => x+x;
}
class E = D with K; 复制代码
会收到一个警告,由于 E 没有 twice()
方法,所以不知足 I 和 S,也就不能使用预期的 K 了。
若是一个类有泛型参数,那么它的 mixin 也必须有相同类型的参数。
在 1.13 版本以后,Mixin 能够继承普通类而不要求必定是 Object,Mixin 可使用 super.method()
。
能够经过 operator
关键字重载包括 +
,>
,==
在内的操做符,但要注意,重载 ==
操做符的同时也要重载 hashCode
的 getter 方法:
class Person {
final String firstName, lastName;
Person(this.firstName, this.lastName);
// Override hashCode using strategy from Effective Java,
// Chapter 11.
@override
int get hashCode {
int result = 17;
result = 37 * result + firstName.hashCode;
result = 37 * result + lastName.hashCode;
return result;
}
// You should generally implement operator == if you
// override hashCode.
@override
bool operator ==(dynamic other) {
if (other is! Person) return false;
Person person = other;
return (person.firstName == firstName &&
person.lastName == lastName);
}
}
复制代码
使用 enum
关键词定义枚举类型,枚举类型的值有一个 index
getter,枚举值的列表能够经过它的 values
常量获取,枚举支持 switch
语句,你不能继承,mix in 或者实现枚举类,不能显式实例化枚举:
enum Color { red, green, blue }
assert(Color.red.index == 0);
List<Color> colors = Color.values;
复制代码
每一个 Dart app 都是一个 library,即便它没使用 library
指令,library 不只提供 API,仍是个隐私单元,如下划线(_)开头的标识符只在当前 library 内可见。
使用 import
指令能够指定在另外一个 library 范围内如何使用另外一个 library 的命名空间。对于内置的 library,URI 是特定的 dart:
scheme,而其余的 URI 能够是文件系统路径或者 package:
scheme:
import 'dart:html';
import 'package:test/test.dart';
复制代码
若是两个 library 的标识符冲突,可使用前缀加以区分:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = new Element();
// Uses Element from lib2.
lib2.Element element2 = new lib2.Element();
复制代码
若是只想引入 library 的部分功能,须要使用 show
或 hide
指定:
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
复制代码
若是想要懒加载某个 library,先在 import 时经过 deferred as
指定 library 的标识符,而后在须要使用它的时候调用标识符的 loadLibrary()
:
import 'package:greetings/hello.dart' deferred as hello;
...
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
复制代码
此时 await
关键字会暂停程序执行,直到 library 加载成功,能够屡次调用 loadLibrary()
,由于 library 只会加载一次。
Dart 为异步提供了 Future
和 Stream
两个类,Future 就像未来某个时刻提供结果的承诺,Stream 是获取值序列(例如事件序列)的一种方式。
不少时候没有必要直接使用 Future 或 Stream API,而是使用 async
和 await
关键字进行异步编程,这样更容易理解,并且代码看起来也更像同步代码。
await
必须在异步函数中使用,用来等待异步函数返回的结果,如:
Future checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}
复制代码
虽然异步函数能够执行耗时操做,但它不用等待耗时操做完成,这个异步函数会执行直到遇到第一个 await
表达式,而后返回 Future 对象,在 await
表达式执行完后再继续执行。
能够在一个异步函数里 await
屡次,也能够 try/catch await
的代码。
在 await expression
语句中,expression
的值一般是个 Future,若是不是,那么它会被自动包装成 Future。await expression
会暂停其以后代码的执行,直到返回的对象可用。
若是异步函数不须要返回有用值,能够那么返回类型应该是 Future<void>
。
能够用 await for
异步等待 stream 中的全部结果,但不能是 UI 事件流这样的 stream,由于这样的 stream 是无限的。
Dart 内置了两种生成器函数,同步的生成器函数返回 Iterable
对象,异步的生成器函数返回 Stream
对象,使用 sync*
/async*
指定同步异步,使用 yield
交付值,还能够经过 yield*
递归生成:
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
复制代码
自定义注解就像声明普通类同样:
library todo;
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
复制代码
as
子句)要所有小写并用下划线分隔。HttpConnectionInfo
uiHandler
IOStream
HttpRequest
Id
DB
复制代码
lib
目录下的 library时,要使用相对路径。${expression}
插值便可。.length
判断集合是否为空可能会很是慢,因此使用 .isEmpty
或 .isNotEmpty
判断集合状况是最好的选择,更快也更直观。var copy1 = iterable.toList();
var copy2 = new List.from(iterable);
复制代码
第一种能够保存原始对象的类型参数,而第二种的运行时类型是 dynamic
的,不过能够经过 new List<int>.from(numbers)
这样的方式指定。whereType()
:var objects = [1, "a", 2, "b", 3];
var ints = objects.whereType<int>();
复制代码
cast()
,由于该方法会返回 lazy 集合而且每次操做都要检查元素类型,若是集合元素和元素操做特别多那么 lazy 验证和打包的开销会特别大,因此 stuff.toList().cast<int>()
彻底能够用 new List<int>.from(stuff)
替代,stuff.map((n) => 1 / n).cast<double>()
彻底能够用 stuff.map<double>((n) => 1 / n)
替代。=>
简化简单的代码逻辑,如:num get x => center.x;
set x(num value) => center = new Point(value, center.y);
bool isReady(num time) => minTime == null || minTime <= time;
复制代码