导图社区 Dart 语言概述(第二部分)
由 Dart 官方教程总结而来,共分为两个部分,此为第二部分,主要包括流程控制、异常、类、枚举、泛型、库和可见性、异步、并发、生成器、元数据等内容
编辑于2022-07-30 14:40:46Dart (Part II)
流程控制
if else、while、do while、break、continu 与其他语言无异
for 循环可用 C 风格的 for (var i = 0; ...; ...) { };也可使用 for (final foo in bar)
可迭代对象还可以使用 forEach()
Dart 的 switch 必须有 break,否则会报错;若要 fallthrough,则可以用空的 case,并忽略 break
* 在非空 case 中实现 fallthrough 可以使用 continue 配合 label 实现var foobar = 1;switch (foobar) { case 1: doSth(); continue somewhereElse; somewhereElse: case 2; doSth2(); break;}
断言 assert(表达式, opts); 用于当表达式为 false 时中断代码执行,并抛出异常,此语句仅在开发模式下可用,生产环境中被忽略
异常
抛出异常:Dart可以将任何非 null 对象作为异常抛出,但建议抛出 Error 或 Exception 类型的异常,语法: throw Exception(...);
捕获异常:使用 try 来包裹可能抛出异常的代码,on 指定异常类型,catch 获取异常对象及栈信息 StackTrace (https://api.dart.cn/stable/dart-core/StackTrace-class.html)
rethrow 可以将捕获的异常再次抛出
无论是否抛出异常,finally 语句都会执行,这通常用于做一些 clean up
类
实例变量
所有未初始化的实例变量的值都为 null,并且其类型应为可选类型或使用 late 或者在构造函数中初始化
构造函数
声明一个与类名一样的函数可以声明一个构造函数;未声明构造函数时,会自动生成一个无参数的构造函数,并且其会调用父类的无参构造方法
使用 this 引用当前实例,但注意,当且仅当命名冲突时 this 才有意义
构造函数初始化的语法与 C++ 相似,为 ClassName(parameters): pro1 = para1, pro2 = para2; 但其实冒号后的也可以为表达式,做一些运算或使用 assert
final 初始化:一个语法糖,为 非空或 final 变量初始化
例:class Point { final double x; final double y; Point(this.x, this.y); // 这一行}
命名式构造函数:例: class Point { final double x; final double y; Point.origin() // 可使用 Point.origin() 构造 : x = 0.0, y = 0.0;}
注意,构造函数不能被继承,也就是说,如果希望在子类中提供一个与父类命名构造函数一样的命名构造函数,则需要在子类显式地声明
调用父类非默认构造函数
默认情况下,子类会在调用其构造函数体前、初始化列表后调用父类的匿名无参构造方法
若父类无匿名无参构造函数,则子类应在构造函数体前使用冒号指定父类构造函数例:class A { String? foobar; A.fromStr(this.foobar);}class B extends A { B.fromStr(super.foobar) : super.fromStr() { // 这种形式叫超类参数,是一个语法糖;这里也可以 B.fromStr(String sth) : super.fromStr(sth) print("Init"); }}
常量构造函数
如果类生成的对象是不变的,可以在构造函数前加上 const 并确保所有实例变量为 final 来实现常量构造函数(这在 Flutter 非常常用)
工厂构造函数
使用 factory 关键字来使构造函数变为工厂构造函数注意:在工厂构造函数中无法访问 this
* 工厂构造函数的使用场景(文章的“单例”打错了) https://juejin.cn/post/6966099469527941156
* 重定向构造函数用于调用同类中的其他构造函数例:class Point { double x, y; Point(this.x, this.y); Point.origin() : this(0, 0); // 使用 this}
方法
实例方法与其他语言无异,可以参考 Part I 的函数部分
重写(载)操作符:使用 operator 关键字,与 C++ 类似
Getter 和 Setter:在读或写属性(Swift 中叫计算属性)时执行的方法例:class Rect { double left, width; Rect(this.left, this.width); double get right => left + width; set right(double value) => left = value - width;}
抽象类
使用 abstract 标识类来创建 抽象类,抽象类默认无法被实例化若要让抽象类支持实例化,可以在其中添加工厂构造函数
抽象方法:实例方法和 Getter、Setter 都可抽象,使用分号代替方法体可声明一个抽象方法
实现抽象类:使用 extends 关键字
隐式接口(想要创建一个B类支持A类的API,但不想继承自A类)
每一个类都隐式地定义了一个接口并且实现了该接口
B类实现A类接口使用 implements 关键字例:class A { final String _foobar; A(this._foobar); String getBaz() => _foobar;}class B implements A { String get _foobar => "BBB"; String getBaz() => "This is $_foobar";}
扩展(创建子类或在原有内容的基础上增加接口)
扩展类(继承):使用 extends 关键字,super 可引用一个父类
扩展方法:语法为 extension Foobar on Type { ... }
重写(子类重写父类的方法)
子类可以重写父类 的实例方法、Getter和Setter,使用 @override 标记一个方法来向编译器注释重写了一个成员例:class A { int get foobar => 1;}class B extends A { @override int get foobar => 2;}
* 若重写 == 操作符,则必须同时重写 hashCode 的 Getter 方法
注意:1. 重写的方法的返回值应与原方法的返回值类型一致或为子类型2. 重写的方法的参数个数与原方法一致,参数类型与原方法的一致或为父类型3. 泛型的方法不能重写为非泛型的,非泛型的也不能重写为泛型的方法
Mixin
有关 Mixin 可以看 https://juejin.cn/post/6844903858209062920
实现一个 Mixin 需创建一个继承自 Object 并且未声明构造函数的类(例子中的 B),并使用 mixin 关键字替代 class 关键字;还可以用 on 来指定那些类(例子指定了 A)可以使用该 Mixin;使用 with 关键字来使用 Mixin例:class A { ... }mixin B on A { ... }class C extends A with B { ... }
类成员(静态成员)
使用 static 关键字声明类变量或类方法;使用 ClassName.member 访问类成员
注意:静态变量在其首次被使用时初始化;静态方法无法访问 this,但是可以访问静态变量;根据文档建议,常用的函数应声明为顶级函数而非类方法
* Call 方法
通过实现类的 call() 方法,允许使用类似函数调用的方式来使用该类的实例
元数据
以 @ 开头,后紧跟一个编译时常量或调用一个常量构造函数,例如:@override、@Deprecated、@deprecated
元数据可以在 library、class、typedef、type parameter、 constructor、factory、function、field、parameter 或者 variable 声明之前使用,也可以在 import 或 export 之前使用
* 可使用反射在运行时获取元数据信息
生成器
当需要延迟地生成一连串值时使用;Dart 支持同步和异步生成器
使用 sync*、返回 Iterable 创建同步生成器;使用 async*、返回 Stream 实现异步生成器
使用 yield 传递值,yield* 优化递归性能
并发
Dart 使用 isolate 代替线程,每个 isolate 都有自己的堆内存以确保其状态私有
异步
使用 async 和 await 关键字
可以使用 try catch 语块处理 await 导致的异常
异步的函数返回 Future 或 Stream
从 Stream 中获取值可以使用 await for 关键字或使用 Stream API
库和可见性
使用下划线开头的成员仅在代码库中可见
使用 import 引入一个库,使用 as 为库提供一个别名,使用 show、hide 来导入库的部分内容
泛型
使用尖括号 (<>)
Dart 的泛型是固化的,在运行时也会保留类型信息
限制泛型的类型:指定参数类型为某一类的子类,使用 extends 关键字例:class Foobar<T extends Baz> { ... } // 只能传入 Baz 或其子类型
* 此时也可以不传类型,则默认为 "Baz"
枚举
Dart 的枚举是继承自 Enum 类的
* 这意味着可以使用与定义类类似的语句来增强枚举
可以通过 .name 属性获取一个枚举值的名称