https://dart.dev/guides/language/language-tour

Dart,和最近的其他语言一样,是一门不断发展的,并且发展速度很快的语言。

基础概念

  • 一切通过变量引用的都是对象,一切对象都是某一个class的实例,一切class都是Object class的子类。
  • Dart是强类型语言,但是Dart可以推断类型,因此类型声明是可选的。如果不希望为变量确定类型,可以使用dynamic类型。
  • Dart支持泛型,List List。。。
  • Dart支持顶层函数,也支持类方法或者实例方法,还支持在函数中嵌套定义函数。
  • Dart支持顶层变量,也支持类变量或者实例变量。
  • Dart没有public、protected、private关键字。默认所有变量都是public的,以下划线_开头的变量是仅Library内可访问的。
  • Dart语言有“expression”和“statement”的概念,expression具有运行时值,statement没有,一个cond ? a : b 三元运算符是expression,具有运行时值,而一个if语句是statement,没有运行时值。statement可以包含expression,而expression不可以直接包含statement。【if里面可以包含各种expression,也可以嵌套包含if,但是expression里面通常不能直接包含if。。。】
  • Dart工具会报告两类问题:warning和error。warning表示你的代码可能无法工作,但并不阻止编译运行;error分编译时和运行时,编译时error阻止代码编译运行,运行时error代码正在运行时抛出exception。
  • dart中使用 == 做值相等判断,可以在自己的类中重写 operator== 来定义自己的相等判定方法,如果想要判断引用是否相同,需要使用 isIdentical 方法。

关键字

Dart中,语言的关键字并不全是保留字:

  • 部分关键字是contextual keyword,仅在特定的上下文中有特殊含义,没有处于特殊上下文时它们是合法的identifier;
  • 部分关键字是built-in identifier,也就是预定义的标识符。为了简化JS到Dart的移植,这些关键字大多数情况下都是合法的identifier,但是不能被用作class name、type name或import prefix;
  • 部分关键字是Dart 1.0之后添加的asynchrony support相关的关键字,仅在async/sync上下文中受限;
  • 其他关键字都是保留字,任何情况下不可作为identifier。

变量定义

变量声明时可以使用var,让Dart推断类型:
var name = “gaolf”;
也可以写出变量类型:
String name = “gaolf”;
在希望变量类型不受限制时,要使用Object或者dynamic作为类型:
dynamic name = “gaolf”;

推荐尽可能使用var,让Dart推断类型。

变量默认值

Dart中仅对变量做声明时,Dart会使用null作为变量值为变量赋值,由于Dart中一切都是变量都引用对象,因此int类型变量也会被默认赋值为null。

常量声明:final和const

如果变量引用永不改变,应该使用final或者const修饰变量声明,使用final或者const并且省略类型时,Dart会自动推断变量类型。
final变量仅能被赋值一次,const变量则是编译时就确定的常量。
顶层的final变量或者定义在类上的final变量会被懒初始化,仅在它第一次被使用到时才会初始化。

const不仅仅可以用来修饰变量声明,还可以用来修饰值。一个类型为V的变量,可以引用一个类型为const V的值。

内置类型

Dart语言中,以下类型都有对应的字面量表达式:

  • number - Dart中,定义了num基础类型,唯一的两个子类就是int, double
    • int -DartVM中int范围是-2^63~2^63-1,如果Dart被编译为JS,int对应JS中的number类型,范围为-2^53~2^53-1
    • double - IEEE 754 64位浮点数
  • String - 可以使用单引号和双引号,二者无区别;连续的三个单引号或者双引号可以表示多行字符串;字符串前面加r,如r’xxx’,表示原始字符串,忽略了一切转移字符
  • bool - 唯一的两个值为true / false,Dart中任何其他类型都不可隐式转换为bool类型
  • List - 使用方括号定义List实例,[1, 2, 3]
    • 可以使用数字下标访问List实例中的元素:list[1]
    • 可以使用…或者…?操作符将list展开,如:[1, 2, …list],带问号的版本会在list为null时展开为空而不是抛出异常
    • 定义List实例时
      • 可以使用collection if,如:var nav = [‘Home’, if (cond) ‘Outlet’];
      • 可以使用collection for,如:var listOfStr = [‘#0’, for (var i in listOfInt) ‘#$i’];
  • Set - 使用大括号定义Set实例,{1, 2, 3}
    • 由于Set字面量和Map字面量的相似性,定义空Set时需要这样定义:{},否则会被Dart解释成空Map类型:Map
    • 和List一样,Set支持…和…?操作符
  • Map - 使用大括号和冒号定义Map实例:{‘first’: 1, ‘second’: 2}
    • 可以使用key作为下标读取或修改Map实例元素
    • 读取不存在的key不会抛出异常,只会取到null
  • Runes
    • Runes允许访问字符串的Code Point,即按照Unicode字符来访问字符串,而不是把字符串当作固定长度字符组成的数组
    • Dart字符串以UTF-16为编码,在字符串中可以使用\uXXXX表示Unicode code point,超出4个十六进制表示范围的Unicode可以使用\u{XXXXXX}表示
    • 如果需要读写字符串中的单个Unicode字符,要使用字符串的characters getter属性,否则直接使用String的API可能会截断Unicode字符。
  • Symbol - 使用#后面跟operator或者identifier定义Symbol实例
    • symbol用于表示一个operator或者identifier,正常情况下不需要使用,当使用反射特性时需要使用symbol

函数

Dart中,函数是对象,类型是Function。Function可以直接作为变量被赋值,或者被作为参数传递。Dart中有Callable class,这种类型的对象可以直接被当作函数以函数调用语法来调用。

函数的声明和定义:

bool functionName(int atomicNumber) {
return true;
}

  • 函数的返回类型和参数类型都可以省略;
  • 只有一行return语句作为body的函数可以利用 => 简写:
    • bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
  • 函数可以有两类参数:必须的和可选的
    • 必须的参数,即正常列出的参数
    • 可选的参数可以是命名参数或位置参数二者之一:
      • 命名参数使用大括号定义:enableFlags({bool bold, bool hidden})
        • 通过使用@required注解修饰参数声明,可以将参数标记为必须的
      • 位置参数使用方括号定义:say(String from, [String device]) {if (device != null} ……}
      • 可选参数都可以有默认值:void enableFlags({bool bold = false}) {……}
        • 之前Dart通过冒号指定默认值,但现在这种语法已经被deprecate了

main()函数

每个应用都必须有一个顶级的main()函数作为入口点。main返回类型为void,并且有一个可选的List参数。如果要解析参数,可以使用args library。

匿名函数/lambda/closure

匿名函数被称作lambda或者closure,匿名函数的定义方式如下:
(String param) {

}
和普通函数定义类似,当匿名函数body仅有一个表达式组成时,可以直接使用=>简写。
匿名函数可以引用上下文中的变量,变量值在函数对象初始化时便被捕获。

函数的返回值

Dart中一切函数都有返回值,如果不写return语句,Dart会自动地补充一句return null。

自定义对象类型作为函数

任何类型,只要定义了call方法,就会被dart视为Function的子类,此时这个类型的对象既具有函数类型,又具有原本定义的类型。
例子:flutter-redux中的TypedReducer类型,因为定义了call方法,并且

操作符

Dart允许操作符重载。
as, is, is!在Dart中属于Type test operator。
??是Dart的null-safe操作符,如果第一个操作数非null,则返回第一个操作数,否则返回第二个操作数;
?.是Dart的null-safe方法调用操作符,当被调用的变量是null时直接返回null;
..是Dart提供的cascade notation,并不算一个操作符,但官方文档还是把它还是放在这里介绍了。。。允许在不定义变量的情况下连续调用变量上的方法或访问变量的属性,因此在dart中是没有什么理由返回this的,毕竟这样做的唯一目的就是链式调用,而dart已经从语言层面支持了这种特性:

  1. querySelector('#confirm') // Get an object.
  2. ..text = 'Confirm' // Use its members.
  3. ..classes.add('important')
  4. ..onClick.listen((e) => window.alert('Confirmed!'));
  5. // 相当于:
  6. var button = querySelector('#confirm');
  7. button.text = 'Confirm';
  8. button.classes.add('important');
  9. button.onClick.listen((e) => window.alert('Confirmed!'));

循环

Dart支持对iteration对象使用for-each:for (var x in collection);
iterable接口定义了一系列方便操作集合的方法,包括forEach()和where()。

switch case

Dart支持switch case语法,语法看起来和Java完全一致,每一个case中的表达式必须是编译时常量,Dart使用==比较switch和case,判断的类型不可以重载==操作符。
Dart在switch case的语境下提供了continue关键字用于显式fall-through,除非一个case没有body,不然隐式的fall-through是被禁止的。

Assert

Dart使用assert在开发期间确保应用符合预期:assert(cond, [可选msg])
assert是否生效取决于工具的配置:

  • Flutter在debug mode启用assert;
  • dartevc默认启用assert;
  • dart和dart2js根据—enable-asserts命令行flag决定是否启用assert

异常

Dart源码可以抛出和捕获异常,没有被捕获的异常会导致isolate被终止。
除了捕获异常的语法比较特殊以及增加了rethrow关键字、并且支持throw任意类型(一般不会这么干),其他的try, catch, throw, finally关键字的使用都符合Java程序员的预期。
Dart中一切异常都是unchecked exception。因此,方法的异常规格需要通过文档来定义。
Exception和Error类型是Dart提供的异常类型。不过,Dart允许抛出任意类型的值作为异常,不过的不过,代码不应该抛出除了Exception和Error以外类型的值。
此外,Dart的try-catch语法有些特殊:

  1. // 使用on表示要捕获的类型,不写on表示捕获全部类型
  2. // 使用catch捕获异常值,不关心被抛出的异常可以不写catch
  3. try {
  4. breedMoreLlamas();
  5. } on OutOfLlamasException {
  6. // A specific exception
  7. buyMoreLlamas();
  8. } on Exception catch (e) {
  9. // Anything else that is an exception
  10. print('Unknown exception: $e');
  11. } catch (e) {
  12. // No specified type, handles all
  13. print('Something really unknown: $e');
  14. }
  15. // catch允许定义第二个参数,此时第二个参数是异常调用栈
  16. try {
  17. // ···
  18. } on Exception catch (e) {
  19. print('Exception details:\n $e');
  20. } catch (e, s) {
  21. print('Exception details:\n $e');
  22. print('Stack trace:\n $s');
  23. }
  24. // 此外,Dart支持rethrow关键字重新抛出异常
  25. // finally关键字就和Java一样

Dart支持基于类和基于mixin的继承。Dart 2.0开始,new关键字可以在创建类实例时被省略。

构造函数

Dart中的构造函数是类名,或者<类名>.<标识符>,后者会调用named construcor。
子类不继承父类的构造函数。默认构造函数没有参数,并且会调用父类无参构造函数。

constant构造函数

可以在创建新对象时,在构造函数前加上const关键字来创建常量对象,常量对象会自动应用“享元模式”,即完全相同的常量对象实际上会是同一个对象的引用。
当在创建一个复合的常量对象时,如果最外层的对象已经使用const修饰了,那内层的对象构造就可以省略const修饰,dart会认为const对象构造过程中涉及到的对象的构造也是const的,这被称为“常量上下文”。

runtimeType属性

使用对象的runtimeType属性可以获得对象的运行时类型,即一个Type类型的对象。

类的声明和定义

  • 基本类型定义语法:

    1. class Point {
    2. // 属性定义:
    3. num x; // default to null
    4. num y = 0;
    5. // 构造函数定义:
    6. // 以下两种constructor定义等价,后者是语法糖,实际开发中最好选用后者,语法更简洁
    7. // Point(num x, num y) {
    8. // this.x = x;
    9. // this.y = y;
    10. // }
    11. Point(this.x, this.y);
    12. // named constructor,通过<类名>.<标识符>定义
    13. Point.origin() {
    14. x = 0;
    15. y = 0;
    16. }
    17. // 方法定义:
    18. num distanceTo(Point other) {
    19. var dx = x - other.x;
    20. var dy = y - other.y;
    21. return sqrt(dx * dx + dy * dy);
    22. }
    23. // 使用get和set关键字定义getter和setter方法
    24. num get left => x;
    25. set left(num value) => x = value;
    26. }
  • 父类构造函数调用

    1. class Employee extends Person {
    2. Employee() : super.fromJson(defaultData);
    3. // ···
    4. }
  • initializer list

    1. // Initializer list sets instance variables before
    2. // the constructor body runs.
    3. Point.fromJson(Map<String, num> json)
    4. : x = json['x'],
    5. y = json['y'] {
    6. print('In Point.fromJson(): ($x, $y)');
    7. }
    • 可以在initializer list中写assert以确保参数的正确性
    • 类实例final属性可以通过initializer来赋值
  • 构造函数转发

    1. class Point {
    2. num x, y;
    3. // The main constructor for this class.
    4. Point(this.x, this.y);
    5. // Delegates to the main constructor.
    6. Point.alongXAxis(num x) : this(x, 0);
    7. }
  • constant构造函数

    1. // 一个所有属性都是final的类型可以声明constant constructor,
    2. // DartVM针对这种类型实例自动在const上下文中应用享元模式
    3. class ImmutablePoint {
    4. static final ImmutablePoint origin =
    5. const ImmutablePoint(0, 0);
    6. final num x, y;
    7. const ImmutablePoint(this.x, this.y);
    8. }
  • factory构造函数

    1. // 使用factory关键字修饰constructor,可以从constructor返回来自缓存的对象,或是返回子类类型的对象
    2. // factory只不过是语法糖,factory方法中无法访问this
    3. // 在调用factory constructor时,new关键字就很奇怪了,不知道Dart允许不使用new是否有这个考虑?
    4. class Logger {
    5. final String name;
    6. bool mute = false;
    7. // _cache is library-private, thanks to
    8. // the _ in front of its name.
    9. static final Map<String, Logger> _cache =
    10. <String, Logger>{};
    11. factory Logger(String name) {
    12. return _cache.putIfAbsent(
    13. name, () => Logger._internal(name));
    14. }
    15. Logger._internal(this.name);
    16. void log(String msg) {
    17. if (!mute) print(msg);
    18. }
    19. }

类实例初始化顺序

  1. initializer list
  2. superclass’s constructor
  3. main class’s constructor

如果父类定义了无参默认构造函数,那么可以在子类的构造函数定义中省去对父类构造函数的调用,Dart会自动插入调用父类构造函数的代码。

抽象类和抽象方法

实例方法、getter和setter方法可以是abstract的,只有abstract class中可以定义abstract method。要定义abstract method,只要在abstract class中正常写出方法签名,并不写出body即可:

  1. abstract Doer {
  2. void doSth();
  3. }
  4. class RealDoer extends Doer {
  5. void doSth() {
  6. ...
  7. }
  8. }

隐式Interface

Dart的每一个class都隐式定义了一个interface,class的所有成员 - 方法、getter/setter - 定义了这个class的隐式接口。
构造函数不属于隐式接口。

  1. // A person. The implicit interface contains greet().
  2. class Person {
  3. // In the interface, but visible only in this library.
  4. final _name;
  5. // Not in the interface, since this is a constructor.
  6. Person(this._name);
  7. // In the interface.
  8. String greet(String who) => 'Hello, $who. I am $_name.';
  9. }
  10. // An implementation of the Person interface.
  11. class Impostor implements Person {
  12. get _name => '';
  13. String greet(String who) => 'Hi $who. Do you know who I am?';
  14. }
  15. String greetBob(Person person) => person.greet('Bob');
  16. void main() {
  17. print(greetBob(Person('Kathy')));
  18. print(greetBob(Impostor()));
  19. }

继承

使用extends关键字,可以不仅获得类的接口,同时还获得类中所定义的接口实现。在子类中,可以通过super访问父类成员。

Override

子类可以重载父类的方法、getter/setter。使用@override可以告诉dart你的意图是重载,从而在实际没有发生重载时dart可以给出提示。

covariant

convariant用在希望将重载的方法参数类型变窄的场景,下面的例子中convariant被放在了子类,但它也可以放在父类方法参数前,以表明子类可以收窄该参数类型。

  1. class Animal {
  2. void chase(Animal x) { ... }
  3. }
  4. class Mouse extends Animal { ... }
  5. class Cat extends Animal {
  6. void chase(covariant Mouse x) { ... }
  7. }

操作符重载

  1. class Vector {
  2. final int x, y;
  3. Vector(this.x, this.y);
  4. Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  5. Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
  6. // Operator == and hashCode not shown. == 和hashCode需要保持一致
  7. // ···
  8. }
  9. void main() {
  10. final v = Vector(2, 3);
  11. final w = Vector(2, 2);
  12. assert(v + w == Vector(4, 5));
  13. assert(v - w == Vector(0, 1));
  14. }

noSuchMethod()

当在一个对象上调用一个没有定义的方法时,如果对象override了noSuchMethod方法,那么该方法会被调用。不过通常来说dart不会让我们写出调用不存在对象方法的代码,除非:

  • receiver的静态类型是dynamic
  • receiver的静态类型是一个override了noSuchMethod()方法的类型

Extension methods

扩展方法,和kotlin的概念类似,其实也是语法糖,可以把原本通过Util类提供的工具方法直接和特定类型关联。
https://dart.dev/guides/language/extension-methods

枚举类型

使用enum关键字定义枚举类型,枚举类型实例的index属性表明它们的枚举值,枚举类型上的values属性是所有枚举实例按顺序组成的List。

  1. enum Color { red, green, blue }
  2. assert(Color.red.index == 0);
  3. assert(Color.green.index == 1);
  4. assert(Color.blue.index == 2);

mixin

mixin是在多个类继承关系中复用特定类的代码的方式。使用with关键字将类mixin到当前类:

  1. class Musician extends Performer with Musical {
  2. // ···
  3. }
  4. class Maestro extends Person
  5. with Musical, Aggressive, Demented {
  6. Maestro(String maestroName) {
  7. name = maestroName;
  8. canConduct = true;
  9. }
  10. }

使用mixin定义一个可以被mixin的类,使用on表明该mixin仅可以被指定的类mixin:

  1. mixin Musical {
  2. bool canPlayPiano = false;
  3. bool canCompose = false;
  4. bool canConduct = false;
  5. void entertainMe() {
  6. if (canPlayPiano) {
  7. print('Playing piano');
  8. } else if (canConduct) {
  9. print('Waving hands');
  10. } else {
  11. print('Humming to self');
  12. }
  13. }
  14. }
  15. mixin MusicalPerformer on Musician {
  16. // ···
  17. }

定义在类上的成员

使用static关键字可以将属性或者方法定义在类上,而不是类实例上。
static属性具有懒加载的特性,直到被使用时才会执行static属性的初始化逻辑。
除了懒加载和Java不一样,相关语法和Java完全一致。不过Dart在定义static const变量时,倾向于不使用Java的大写字母+下划线的命名,而是按照普通变量命名方式:开头小写后续单词首字母大写命名。

泛型

集合字面量与泛型

可以在集合字面量前指定泛型类型以约束集合字面量的类型。

  1. var names = <String>['Seth', 'Kathy', 'Lars'];
  2. var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
  3. var pages = <String, String>{
  4. 'index.html': 'Homepage',
  5. 'robots.txt': 'Hints for web robots',
  6. 'humans.txt': 'We are people, not machines'
  7. };

调用泛型构造函数

  1. var nameSet = Set<String>.from(names);
  2. var views = Map<int, View>();

Dart的泛型语法和Java的几乎完全一致,包括类定义中定义泛型、泛型方法、通过extends指定泛型上界:

  1. // 泛型类型可以指定上界
  2. class Foo<T extends SomeBaseClass> {
  3. // Implementation goes here...
  4. String toString() => "Instance of 'Foo<$T>'";
  5. }
  6. class Extender extends SomeBaseClass {...}
  7. // 使用class进行实例创建的代码
  8. var someBaseClassFoo = Foo<SomeBaseClass>();
  9. var extenderFoo = Foo<Extender>();
  10. var foo = Foo(); // Instance of 'Foo<SomeBaseClass>'
  11. // 泛型方法定义:
  12. T first<T>(List<T> ts) {
  13. // Do some initial work or error checking, then...
  14. T tmp = ts[0];
  15. // Do some additional checking or processing...
  16. return tmp;
  17. }

库和可见性

import和library指令允许开发者创建模块化的代码并分享。库除了提供API还提供了可见行的保护,以下划线开头的标识符不会被导出,仅在库内可见。
每一个Dart app都是library,即使没有使用library指令。
使用packages来分发Library:https://dart.dev/guides/packages

使用library

用import指令将库的标识符导入到当前上下文,如:import ‘dart:html’;
import指令指定一个标识特定库的URI。内置库的URI以特殊的schema:’dart:’ 开头。对于其他的库,需要使用文件系统路径或者 ‘package:’ 作为schema的URI。package scheme用于引用由package manager如pub tool提供的库。

指定library prefix

可以使用 import as ; 的语法为引入的库符号限定在特定命名空间中。

  1. import 'package:lib1/lib1.dart';
  2. import 'package:lib2/lib2.dart' as lib2;
  3. // Uses Element from lib1.
  4. Element element1 = Element();
  5. // Uses Element from lib2.
  6. lib2.Element element2 = lib2.Element();

部分引入库

可以使用:

  • import show ; 仅引入指定identifier
  • import hide ; 引入除了指定identifier以外的所有库identifier

库的懒加载

这个特性目前是专门给Web App用的,仅被dart2js支持,DartVM和Flutter都不支持。

创建库

https://dart.dev/guides/libraries/create-library-packages

  • 如何组织库代码
  • 如何使用export指令
  • 如何使用part指令
  • 如何使用library指令
  • 如何使用条件import和export来让库同时支持多个平台

Dart生态使用pub package manager作为标准的库管理工具,所有发布的库都在pub.dev网站上寄存,也可以使用本地文件系统或者github上的库,但这样的库别人无法从pub.dev上找到,不利于推广。pub会和maven、gradle一样,根据一个pubspec文件 - pubspec.yaml文件下载各个库,管理各个库的版本和依赖。
pubspec文档:https://dart.dev/tools/pub/pubspec
要触发依赖的下载安装,运行pub get命令。pub会将所有依赖安装到一个系统缓存目录,然后依赖那里的库。当第一次运行pub get时,pub会创建一个.packages隐藏文件,记录每个库的版本,后续安装库时都会参考这里的版本,不回随意安装新版本。要想更新版本,需要使用pub upgrade,该命令将所有库更新到最新版本,命令后面可以指定特定库。

我们使用的库基本都是library package。一个library package的基础目录结构如下:

  • root dir
    • lib
      • file.dart
    • pubspec.yaml

pubspec.yaml就是前面提到的库配置,而lib目录下的所有dart文件就是库的公共代码,所有public API都应该在这里定义。通常会将具体实现放在lib/src目录下,这个目录下的文件会被视作是private的。不过,也可以通过在lib下写一个使用export的dart文件,将lib/src目录下的文件API导出成公共的。当我们在写 import ‘package:mylib/mylib.dart’ 时,就是在引入mylib库下的lib/mylib.dart导出的所有API。
可以使用条件export/imprt,来在指定库存在的情况下export或import指定库:

  1. // 第一行指定fallback,当下面两行都不匹配时,fallback为export第一行指定的库
  2. // 后面两行按顺序检查,一旦匹配,就export指定库并不再继续检查。
  3. // 将export关键字换成import,就是条件import语法
  4. export 'src/hw_none.dart' // Stub implementation
  5. if (dart.library.io) 'src/hw_io.dart' // dart:io implementation
  6. if (dart.library.html) 'src/hw_html.dart'; // dart:html implementation

异步支持

返回Future或者Stream对象的Dart库方法都是异步执行的,它们会在返回后继续执行需要时间完成的逻辑。
Dart提供async和await关键字来支持异步编程,允许开发者以同步的语法写出异步执行的逻辑。

处理Future

有两种使用Future的方式,分别是:

推荐使用async和await,它们是使用同步风格编写异步逻辑的方式,当使用这种方式编程时,还能够使用try, catch, finally的语意来处理异常。
在形如 await expression 这样的语句中,expression通常都是一个Future,如果不是的话那这个expression会自动的被Dart包装成一个Future。Future代表一个异步返回的对象,await expression会让出当前线程,直到Future成功返回对象后,再重新获得线程的控制权。

声明async函数

Future lookUpVersion() async => ‘1.0.0’;
上面的函数就是一个标准async函数 - 好吧,没那么标准,它直接返回了一个非Future类型的值,让Dart来包装它,此外它也没什么异步逻辑。如果你的异步函数不返回任何值,只要把返回值声明为Future就行。
详细的异步编程指南参考:https://dart.dev/codelabs/async-await

处理Stream

https://dart.dev/guides/libraries/library-tour#stream

Generator

generator是按需生成序列值的方式,Dart提供了两类generator函数:

  • 同步的generator:Iterable
  • 异步的generator:Stream ``` // 要实现同步的generator,需要这样: Iterable naturalsTo(int n) sync* { int k = 0; while (k < n) yield k++; }

// 下面这个是异步的generator: Stream asynchronousNaturalsTo(int n) async* { int k = 0; while (k < n) yield k++; }

// 当你的generator是递归的时,可以使用特殊的yield关键字以提升性能 Iterable naturalsDownFrom(int n) sync { if (n > 0) { yield n; yield* naturalsDownFrom(n - 1); } }

  1. <a name="0okEQ"></a>
  2. ### Callable
  3. 实现call()方法可以让你的Dart对象实例被像一个函数一样的调用。

class WannabeFunction { call(String a, String b, String c) => ‘$a $b $c!’; }

main() { var wf = new WannabeFunction(); var out = wf(“Hi”,”there,”,”gang”); print(‘$out’); }

  1. <a name="o9RVf"></a>
  2. ### Isolates
  3. 如今大多数电脑和移动设备都已经有了多核心处理器,通常开发者为了利用多核处理器的能力,会需要写多线程程序,而多线程则意味着共享内存,多个线程共享相同的资源往往导致复杂的逻辑并招致错误。<br />Dart不支持线程,Dart代码都运行在isolate中,每个isolate都有它自己的内存堆和内存栈,确保isolate不会相互影响。<br />参考资料:
  4. - [Dart asynchronous programming: Isolates and event loops](https://medium.com/dartlang/dart-asynchronous-programming-isolates-and-event-loops-bffc3e296a6a)
  5. - [dart:isolate API reference,](https://api.dart.dev/stable/dart-isolate) including [Isolate.spawn()](https://api.dart.dev/stable/dart-isolate/Isolate/spawn.html) and [TransferableTypedData](https://api.dart.dev/stable/dart-isolate/TransferableTypedData-class.html)
  6. - [Background parsing](https://flutter.dev/docs/cookbook/networking/background-parsing) cookbook on the Flutter site
  7. - [Isolate sample app](https://github.com/flutter/samples/tree/master/isolate_example)
  8. <a name="4jTpJ"></a>
  9. ### Typedefs与函数具体类型
  10. Dart中的函数就是对象,但函数的类型却不太好表示。使用typedef可以为函数类型定义别名,并可以作为参数或返回值类型使用。<br />不用typedef也可以表示函数类型,但代码比较冗余,而且语法、意图不直观,感受一下:
  11. ```dart
  12. class SortedCollection {
  13. // 这里也可以为Function直接声明返回类型和参数类型及个数,但还是推荐使用typedef
  14. // https://api.flutter.dev/flutter/foundation/compute.html
  15. Function compare;
  16. SortedCollection(int f(Object a, Object b)) {
  17. compare = f;
  18. }
  19. }
  20. // Initial, broken implementation.
  21. int sort(Object a, Object b) => 0;
  22. void main() {
  23. SortedCollection coll = SortedCollection(sort);
  24. // All we know is that compare is a function,
  25. // but what type of function?
  26. assert(coll.compare is Function);
  27. }

下面是使用typedef重写的版本:

  1. typedef Compare = int Function(Object a, Object b);
  2. class SortedCollection {
  3. Compare compare;
  4. SortedCollection(this.compare);
  5. }
  6. // Initial, broken implementation.
  7. int sort(Object a, Object b) => 0;
  8. void main() {
  9. SortedCollection coll = SortedCollection(sort);
  10. assert(coll.compare is Function);
  11. assert(coll.compare is Compare);
  12. }

Metadata

Metadata语法上和Java的注解类似,作用上也类似,但是在Dart中,Metadata就是普通的类或者编译时常量,而不像Java需要通过特殊的关键字来定义。
最常见的metadata是@deprecated和@override注解。
metadata可以出现在library,class,typedef,type param,constructor,factory,function,field,parameter,variable,import之前。使用反射可以在运行时获得metadata信息。

注释

普通注释:
// 单行注释
/ 多行注释 /

文档注释

以 /// 或者 /** 开头的是文档注释。文档注释中,可以使用 [identifier] 来引用符号,Dart会正确解析 [identifier] 引用,允许阅读代码的人通过引用跳转,生成的文档中引用会被附上链接。
可以使用Dart SDK的documentation generation tool生成HTML文档,更详细的文档规则:https://dart.dev/guides/language/effective-dart/documentation

Dart核心库

Dart提供了一组功能强大的核心库,包括collection, math, convert等。部分核心库是平台无关的,可以在所有平台上使用,但也有一些平台库是仅限于原生平台的(即非Web平台),或者仅限于Web平台的。