Dart的学习笔记(持续更新)

在Dart中一切都是对象,一个.dart文件可以声明任何对象,包括类、函数、变量;

var

除了局部变量建议用var之外,其余地方建议用具体类型声明;

const

const 修饰一个编译时常量。

  • 当修饰构造函数时,则该类的所有fields必须是final修饰;
  • 当const修饰的是类的field,则必须同时修饰static;
  • 被final或者const修饰的变量,变量类型可以省略;

final

类的fields被final修饰,可以不初始化(只是会警告),但是若要初始化,则必须通过构造函数来初始化,不能通过其它函数初始化。

  • 声明对象时可以省略对象类型;

covariant

因为Dart是类型安全的,所以Override方法时,方法的参数类型不能“收缩(tight)”。但是某些时候需要这种情况时,可以用该关键字注解用来抑制「静态检测错误」(即使知道是无效的),以 通过编译。但是运行时仍会执行无效参数检查。

Class

dart文件

  • 主方法是 main(), 如果需要接收输入的参数则定义为 main(List<String> args)
  • 可以定义任何代码,包括多个类、变量、函数等,无需限定在 class 内部;
  • 没有 public、protected、private 关键字,默认所有的定义都是「public」的,如果需要转化为「private」,直接在对应的命名前增加下划线 _

  • abstract 定义抽象类,使用 extends 则表示继承;

  • 没有 interface 关键字,默认所以的 class 都是隐式的接口。使用 implements 实现接口:

    • 若在同一个文件,则必须实现接口定义的所有变量和函数;
    • 若不在同一个文件,则只需实现「public」类型的变量和函数;

构造函数

  • 提供两种定义方式:

    1. 与类名 (ClassName) 相同,可以理解为 NoNamed constructors

      有且只能存在一个

    2. ClassName.identifier的形式,称作 Named constructors

      可用以提供更具场景化的构造名称,和Java的利用静态方法构造对象的方案类似,因此这种构造方式也就无法被子类继承;

  • 在没有显式提供任何构造函数时,默认包含一个 无参无名 构造函数;不管是哪种方式,一旦显式定义了,默认的无参构造函数就被取代;

  • 可以没有方法体;

  • 构造函数参数区后可继续接 : + 表达式,称为初始化表达式Initializer expressions

    1. 调用父类构造函数,例如:Point.circle(int x) : super.any(x);
    2. 实例化成员变量,例如:Point(int y) : x = 1 + y;
    3. 开发阶段的断言,例如:Point(int y) : assert(y > 0);
    4. 重定向到其它构造函数,例如:Point.circle(int x) : this(x);

      重定向无法和上述前三种共用、没有body、不能快速参数指定

      1, 2, 3可以组合,但是1必须位于整个表达式的最后。

  • factory 关键字定义一个工厂函数(是构造函数,相当于从语言层支持了工厂方法),例如抽象类不能被实例化,可以利用该方式返回子类对象;

  • 如果类实例化的对象不可变(不可修改该对象、且不可修改该对象的变量),可以通过在构造函数前面声明 const、对所有变量声明 final 来将对象转化为编译时常量。

    该形式的构造函数有以下特性:

    1. 没有有body;
    2. 由于所有的变量必须final,对于私有变量,可以通过 初始化表达式 来实例化;
    3. 初始化表达式 涉及的只能是常量;
    4. 任何地方通过该构造函数实例化的 constant对象 (例如const Point(1)这种),如果参数完全一致,那么这些对象是同一个对象;

1. 参数

  • 可直接在参数区使用 this.+ 实例变量名 快速为实例变量赋值,例如:

    1
    Bicycle(this.cadence, this.speed, this.gear);
  • 命名可选参数:将构造函数的部分参数包裹在 {} 中,同时可以为可选参数提供默认值,若参数是一个对象,则默认值需要声明为 const 以表示是一个编译时常量;

  • 位置可选参数:将构造函数的部分参数包裹在 [] 中,传入的参数位置和函数声明的参数位置一一对应。

2. 继承

  • 子类的构造函数必须调用父类的构造函数,默认隐式调用父类的无参无名构造函数(注意:Named constructors 的无参构造函数不算);若父类没有,则需要通过 : super 的形式手动调用,例如:
    1
    Point.circle(int x) : super(x);

实例

  • 定义对象时可以使用 var 关键字,同时也支持 final(如果该对象不会发生变化);
  • 实例化对象时可以省略 new 关键字;

变量

  • 由于完全面向对象,所以任何类型的变量默认值都为null,即使是num;

  • 默认情况每个「public」实例变量都隐式地实现了 getter/setter (无需也禁止再手动实现),如果该实例变量被声明为了「private」,则可以使用关键字 get 定义方法以提供对外(内)访问,set 定义方法提供对外(内)修改;

泛型

  • 与Java的泛型在运行时被擦除不一样的是,Dart的泛型指定后在运行时始终存在:
    1
    2
    3
    var names = List<String>();
    names.addAll(['Seth', 'Kathy', 'Lars']);
    print(names is List<String>); // true

Mixin

  • 可以理解为「组合」,形如Java接口但更强大;
  • 使用方式是通过 class A with B, C, ...,使A得到B、C的方法、字段;
  • 除了普通类,可使用关键字 mixin 定义一种特殊的类,专门表示用来mixin(with)的;

    1. mixin类不能继承或被继承,但是可以(或被)implements
    2. mixin类可以后接 on,例如 mixin A on B ,用以限定 A 只能被 B 及其子类所with
    3. mixin on 所指定的限定类必须包含一个无参无名构造函数;

异常

  • try-catch-finally 捕获异常;
  • 可以在try模块后接 on + Ex 模块来捕获具体类型的异常,区别于 catch 捕获的是具体异常对象;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    try {
    breedMoreLlamas();
    } on OutOfLlamasException {
    // A specific exception
    buyMoreLlamas();
    } on Exception catch (e) {
    // Anything else that is an exception
    print('Unknown exception: $e');
    } catch (e) {
    // No specified type, handles all
    print('Something really unknown: $e');
    }
  • 使用 throw + 字符串 快速抛出异常,也可以继承 Exception 类实现自定义的异常;

  • 使用 reThrow 快速在catch块中再次抛出;

函数式编程

  • 支持函数作为参数;
  • 支持将函数赋值给变量;
  • 由于函数也是对象,所以关键字 Function 如同 num、bool 一样,代表函数的类型,因此,对象可声明的地方,函数也如此,例如:

    1
    2
    3
    4
    5
    void main() {
    Function test() {
    return int abc() => 0;
    }
    }
  • 所有的函数都有返回值,如果没有显式指定return,默认返回null;

  • 函数可以省略声明返回类型,这样就是默认的dynamic类型;
  • => expr 是对 { return expr; } 的一种简写手法,注意:expr指表达式

typedef

  • 也是一种类型;

typedef 用于定义Function类型,具体指参数类型、个数、返回值,而Function的名字只是一个别名,包括以下两种定定义方式:

  1. typedef XXX = void Function(int/String/... x);
  2. typedef void XXX(int/String/... x);

注意:若省略参数名,则参数类型失效,变为了dynamic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef int Test(Object a, Object);
typedef int ABC(Object a, Object b);
typedef ABC2 = int Function(Object a, Object b);
int sort(Object a, Object b) => 0;
main() {
print(Test); // (Object, dynamic) => int
print(ABC); // (Object, Object) => int

print("sort is ABC: ${sort is ABC}"); // true
print("sort is ABC2: ${sort is ABC2}"); // true

print("ABC is ABC2: ${ABC is ABC2}"); // false
print("ABC == ABC2: ${ABC == ABC2}"); // true
}

条件

  • switch case 支持省略 break 以继续下一个 case ,但是前提是这个 case 不包含代码,否则异常;如果确要如此,可以使用 continue + label: 的形式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    switch (command) {
    case 'CLOSED':
    executeClosed();
    continue nowClosed;
    // Continues executing at the nowClosed label.

    nowClosed:
    case 'NOW_CLOSED':
    // Runs for both CLOSED and NOW_CLOSED.
    executeNowClosed();
    break;
    }

external

大意是用于修饰函数,表示该函数的具体实现和它定义的地方是分开的。据目前所知,这种形式常用来针对不同平台作不同的具体实现,例如浏览器端和服务端VM的差异实现。

async / await

https://segmentfault.com/a/1190000007535316

sync/async/yield/yield*

一种(序列)生成器的概念。https://www.dartlang.org/articles/language/beyond-async

  • 类似Async-await概念。用Sync/Async修饰函数,函数被调用时,不同于前者立即返回Future,后者是返回Iterable(同步)/Stream(异步)。

  • 函数内通过yield发射值到迭代器/流,同步的情况是需要外部pull(惰性);异步的情况是stream被订阅后主动push;

  • Sync、ASync是同步和异步的区别,同步情况下,每次yield后会“暂停”,直到下次被pull;异步情况则不会。
  • yield后接一个表达式,yield* 则是后接一个子序列(符合同步异步情况的前提下),并将这个子序列转入当前函数的序列中。