特别点
- 任何能作为变量使用的都是对象, 每一个对象都是一个类的实例. 甚至 数组, 函数, null 都是对象,所有对象都继承自
Object
class. - Dart 是强类型的语言,但是也支持类型推导, 比如
final a = 1
会推导出a
是int
. 还可以使用dynamic
表示类型在运行时才确定 - Dart 支持泛型,例如
List<int>
(一个 int 集合),List<dynamic>
(任何类型的对象的集合). - Dart 支持
top-level
函数, 类实例函数, 类静态函数, 嵌套函数, 局部函数. - Dart 支持
top-level
变量, 类实例变量, 类静态变量. - Dart 只有 library 私有 和 library 公有 两种可见性, 以
_
开头的标识符就代表 library 私有. 比如int _privateNum = 0;
- Dart 支持条件表达式,
condition ? expr1 : expr2
.
特别的关键字
以下是和 Java 比较起来, Dart 中一些特别用途的关键字.
-
dynamic
- 声明一个类型是任意类型的, 表示变量类型在运行时才确定
library 相关
-
show
-
当只导入一个 library 的一部分时使用
-
1 2
// Import only foo. import 'package:lib1/lib1.dart' show foo;
-
-
hide
-
当导入一个 library 中所有内容, 但排除某个时使用
-
1 2
// Import all names EXCEPT foo. import 'package:lib2/lib2.dart' hide foo;
-
-
as
-
给导入的 library 指定别名
-
1 2 3 4
import 'package:lib2/lib2.dart' as lib2; // Uses Element from lib2. lib2.Element element2 = lib2.Element();
-
-
deferred
-
延期导入 library. 当需要使用时再导入, 使用场景:
- 减少应用的启动时间
- A/B Test
- 加载很少会用的的方法
-
-
1 2 3 4 5 6 7 8
// 必须使用 deferred as import 'package:greetings/hello.dart' deferred as hello; // 执行 loadLibrary 真正导入 Future greet() async { await hello.loadLibrary(); hello.printGreeting(); }
-
loadLibrary
即使多次调用, library 也只会导入一次 -
注意
- 延期导入的 library 中的常量, 直到 library 被真正导入后才可用
- 不能使用延期导入的 library 中的类型, 比如
class
等. 最好将包含类型声明的 library 移到一个 libraryA , 然后让 延期导入的 library 和导包的文件都导入这个 libraryA. loadLibrary
函数返回的是一个Future
, 需要注意异步使用.
-
export
- 在一个 dart 文件中批量
export
多个 library, 这样就只用 import 这一个文件
1 2 3 4 5 6 7 8 9 10 11 12 13
// 在 shelf.dart 中声明如下: export 'src/cascade.dart'; export 'src/handler.dart'; export 'src/handlers/logger.dart'; export 'src/hijack_exception.dart'; export 'src/middleware.dart'; export 'src/pipeline.dart'; export 'src/request.dart'; export 'src/response.dart'; export 'src/server.dart'; export 'src/server_handler.dart'; // 最后只用 import shelf.dart 一个包, 就能导入上面所有包
- 在一个 dart 文件中批量
-
part (不推荐使用)
- 拆分一个 library 到多个文件
异步编程支持
-
async
- 结合
await
使用
- 结合
-
await
- 在使用
async
标记了的方法中, 等待一个Future
执行完毕
1 2 3 4 5 6
Future<String> lookUpVersion() => Future.delayed(Duration(seconds: 2), () => "1.0"); void checkVersion() async { var version = await lookUpVersion(); print(version); // 1.0 }
- 在使用
Generators 中使用
- sync
- yield
函数相关
- external
- 声明一个函数的具体实现在其他地方, 目前仅用于 DartVM 在不同平台实现同一个函数.
1 2 3 4 5 6 7 8 9
// 声明 class Foo { external int bar(int x); } // dart2js 中实现 @patch class Foo { @patch int bar(int x) => somethingMagical(x); }
- typedef
- 定义一个函数类型. 当一个函数分配给一个变量时, typedef 能够保留函数的类型信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14
typedef Compare = int Function(Object a, Object b); int sort(Object a, Object b) => 0; class SortedCollection { Compare compare; SortedCollection(this.compare); } void main() { SortedCollection coll = SortedCollection(sort); assert(coll.compare is Function); // True assert(coll.compare is Compare); // True }
- Function
- 声明一个变量是函数类型
类相关
-
factory
- 声明一个构造函数并不总是创建一个新的实例, 也可能返回一个缓存对象, 一个子类对象
- factory 构造函数不能访问
this
1 2 3 4 5 6 7 8 9
factory Logger(String name) { if (_cache.containsKey(name)) { return _cache[name]; } else { final logger = Logger._internal(name); _cache[name] = logger; return logger; } }
-
mixin
减少继承造成的复杂结构和多重继承下的问题
- 在多个类中复用一个类的代码, 而不用继承类(类似: 继承人们的资产, 且不用是他们的孩子)
- 当 继承 或 mixin 的类中有相同的方法时, 优先使用最后
with
的类中的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
class Teacher { void teaching() => print("Something"); } mixin MathTeacher { void teaching() => print("Math"); } mixin EnglishTeacher { void teaching() => print("English"); } class SeniorSchoolTeacher with MathTeacher, EnglishTeacher {} void main() { SeniorSchoolTeacher teacher = SeniorSchoolTeacher(); teacher.teaching(); // English }
-
with
- 声明使用
mixin
的类
- 声明使用
-
convariant
- 允许用子类覆盖重写参数的类型
1 2 3 4 5 6 7 8 9 10
class Animal { void chase(Animal x) { ... } } class Mouse extends Animal { ... } class Cat extends Animal { // 正常情况下,是不允许覆盖参数类型的,会报 Invalid override. 使用 covariant 表明我们知道自己在做什么, 将忽略报错 void chase(covariant Mouse x) { ... } }
-
get set
- 为一个类添加额外的 getter setter 属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class Rectangle { int left, top, width, height; int get right => left + width; set right(int value) => left = value - width; int get bottom => top + height; set bottom(int value) => top = value - height; } void main(){ final rect = Rectangle(3, 4, 20, 15); assert(rect.left == 3); rect.right = 0; assert(rect.left == -20); }
异常
- rethrow
- catch 一个异常之后,再次抛出
-
1 2 3 4 5 6 7 8 9
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. } }
其他
- operator
- 操作符重载. 可以重载常见的操作符.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
class Point { int x, y; Point(this.x, this.y); Point operator +(Point p) => Point(x + p.x, y + p.y); Point operator -(Point p) => Point(x - p.x, y - p.y); } void main() { final p1 = Point(1, 2); final p2 = Point(2, 1); assert((p1 + p2).x == 3); assert((p1 - p2).x == -1); assert((p2 - p1).x == 1); }
变量
- Dart 中的变量也是存储的对象的引用, 当在函数中传递时,与 Java 类似是 值传递.
- 没有初始化的变量默认为
null
, 没有初始化的数字类型也是null
. final
声明不可变的变量. 类中的final
属性, 必须在构造函数的 body 执行前被赋值.const
声明编译期常量.static
声明类变量和类方法, Dart 中的类变量和类方法不能通过类的实例调用
Built-in Type
-
numbers
int
不超过 64 位的整数, 在 DartVM 是-2^63 to 2^63 - 1
, 在编译为 JS 后是-2^53 to 2^53 - 1
double
64 位的双精度浮点
-
strings
- 对应类
String
- 和 Java 类似, 也是不可变的
- UTF-16 编码
- 使用单引号或双引号创建, 使用三个单引号可以写到多行
- 使用
$
将变量嵌入字符串 - 使用
r''
创建原始字符串 ==
操作符, 比较两个字符串的内容是否相等- const 声明的字符串字面量是编译期常量
- 对应类
-
booleans
- 关键字
bool
- 关键字
-
lists (also known as arrays)
- 对应类
List
, 类似 Java 中的ArrayList
- 快速创建
var l = [1, 2, 3, 4, 5];
- 常量list
var constantList = const [1, 2, 3];
常量 list 不能再add
或 修改内部元素
- 对应类
-
sets
- 对应类
Set
- 快速创建
var s = {"dog", "pic", "fish"};
- 对应类
-
maps
- 对应类
Map
- 快速创建
1 2 3 4 5 6
var gifts = { // Key: Value 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings' };
- 对应类
-
runes (for expressing Unicode characters in a string)
- 对应类
Runes
- UTF-32 编码的字符串
- 超过4个数的 uniCode 需要包裹在
{}
里
1 2 3 4 5 6 7 8 9
main() { var clapping = '\u{1f44f}'; print(clapping); // 👏 print(clapping.codeUnits); print(clapping.runes.toList()); Runes input = new Runes('\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}'); print(new String.fromCharCodes(input)); }
- 对应类
-
symbols
Dart中的 symbol 是不透明的动态字符串名称,用于反映库中的元数据。 简而言之,symbol 是一种存储人类可读字符串与优化供计算机使用的字符串之间关系的方法。Reflection 是一种在运行时获取类型元数据的机制,如类中的方法数,它具有的构造函数数或函数中的参数数。 您甚至可以调用在运行时加载的类型的方法。在 Dart 反射中,dart:mirrors 包中提供了特定的类。 此库适用于 Web 应用程序和命令行应用程序。
- 对应类
Symbol
- 感觉主要用在 Dart 的反射上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// foo.dart library foo_lib; class Foo { m1() => print("Inside m1"); m2() => print("Inside m3"); } // main.dart import 'dart:mirrors'; import 'foo.dart'; main() { final libName = Symbol("foo_lib"); final className = Symbol("Foo"); MirrorSystem mirror = currentMirrorSystem(); LibraryMirror libraryMirror = mirror.findLibrary(libName); if (libraryMirror.declarations.containsKey(className)) { ClassMirror classMirror = libraryMirror.declarations[className]; classMirror.instanceMembers .forEach((symbol, methodMirror) => print(symbol)); } }
- 对应类
Functions
- 在 Dart 里, 函数也是对象, 对应类为
Function
. - 简短写法
bool isOdd(int number) => number%2 != 0;
- 在函数的参数声明里可简写为:
T f(T e)
|
|
参数
- 支持两种类型参数, 必传参数和可选参数, 第一个参数为必传,第二个可以为可选参数
- 可选命名参数
|
|
- 可选位置参数
|
|
main 函数
- 程序的入口
- 参数
List<String>
可选
函数作为参数
|
|
变量存储函数引用
|
|
匿名函数
|
|
|
|
如果函数只包含一行,可以使用剪头符号:
|
|
Operators
-
乘除运算符
~/
取模:201 ~/ 100 == 2
/
商: double 结果1 / 100 == 0.01
,100 / 1 == 100.0
%
取余:3 % 5 == 3
-
类型测试符
as
类型转换is
如果对象拥有指明的类型, trueis!
如果对象拥有指明的类型, false
1 2 3
1 is int //true 1 is! int //false 1 is! String //true
-
赋值运算符
??=
如果被赋值的变量是null
, 则将右值赋给它; 否则不进行赋值
1 2 3 4 5
int d = null; d ??= 1; print(d); // 1 d ??= 2; print(d); // 1
-
条件表达式
- 三目运算:
condition ? expr1 : expr2
- 条件赋值:
expr1 ?? expr2
, 如果expr1
不为null
, 就返回expr1
的值; 否则返回expr2
的值
1 2 3
print(null ?? 100); // 100 print(null ?? null); // null print(100 ?? null); // 100
- 三目运算:
-
null 安全访问符:
?.
|
|
- 链式调用符:
..
链式调用对象的多个方法或属性, 最终会返回对象本身
|
|
Class
构造函数
|
|
- 默认构造函数, 没有任何参数
- 带参数构造函数
|
|
- 命名构造函数
|
|
- 初始化列表
|
|
- 重定向构造函数: 调用其他构造函数
|
|
- 常量构造函数: 返回一个编译期常量对象, 这种情况下属性必须是
final
的, 构造函数必须是const
的
|
|
- 工厂构造函数: 可用于不一定返回新对象的情况, 比如:
- 从缓存返回
- 返回一个子类对象
- 单例模式
|
|
方法
实例方法
与 Java 类似, 对象都会拥有实例方法
geter seter
- 会隐式的为类的属性提供
getter
和setter
方法. - 可通过
get
set
关键字提供额外的getter
和setter
(参照上面关键字部分)
抽象方法 abstract method
抽象方法只能存在抽象类中, 且子类(如果子类不是抽象类)必须实现抽象方法
抽象类
与 Java 类似
隐式接口
Dart 为每个类隐式定义了一个接口, 接口包含了类的所有属性和实例方法. 如果你想提供和一个类相同的能力, 而又不想继承它, 那可以直接使用 implements
实现它即可.
|
|
使用 mixin
请移步 “Dart 中的Mixin”
类变量和方法
- 使用
static
声明类变量, 与Java 不同, 类变量只能通过类调用, 不能通过实例调用 - 使用
static
声明类方法, 与Java 不同, 类方法只能通过类调用, 不能通过实例调用
泛型
基本使用与 Java 类似, 但有一个最大不同, Dart 的泛型在运行时不会被擦除, 在运行时可以知道泛型的类型.
Dart:
|
|
Java:
|
|
异步
创建异步方法
返回 Future
或 Stream
的方法就是异步方法, 不需要等待这些方法中进行的耗时操作就可以返回.
|
|
输出:
start
end
async
等待 Future 的结果
- 在
async
方法中使用await
等待一个Future
执行结束
|
|
- 使用
Future
的then
接口
|
|
捕获异常
抛出异常:
|
|
- 使用
try
catch
捕获
|
|
- 使用
Future
的catchError
捕获
|
|
等待 Stream 的结果
使用 Stream:
|
|
- 在
async
方法中使用await for
等待一个循环
|
|
- 使用
Stream
的listen
接口
|
|
Generators 生成器
当需要延迟产生序列值时(不会在内存储存整个序列, 只有当需要下一个值时才计算一下个值), 可以使用 generator
方法, Dart
支持两种 generator 方法:
- 同步的生成器: 返回
Iterable
对象 - 异步的生成器: 返回
Stream
对象
实现同步生成器:
- 方法返回
Interable
- 方法使用
sync*
标记 - 使用
yield
抛出值
|
|
实现异步生成器:
- 方法返回
Stream
- 方法使用
async*
标记 - 使用
yield
抛出值
|
|
如果逻辑是递归的, 可以使用 yield*
优化性能:
|
|
Callable class
为一个类实现 call
方法, 该类就能像一个方法一样被调用.
|
|
Isolates
在 Dart 中, 程序运行在自己的 Isolate
中, 类似 Java 里的 Thread
, 但是 Isolate
彼此独立, 内存不能互相访问, 每个 Isolate
有自己的内存堆.
Typedefs 类型定义
为方法定义一个类型, 当方法被一个变量引用时, 这个类型信息也会能被使用. 比如:
|
|
使用 typedef
:
|
|
原数据 Metadata
类似 Java 中的注解. 可以在运行时通过反射拿到 Metadata
.
定义自己的注解:
|
|
|
|