Dart For Objcer
本文档是一个面向Objcer 的更加精炼的 Dart 语言概览,删除了相同的语法概念,仅剩差异部分,或 dart 独有的部分。未提及的,均可以沿用 objc 经验。
数据类型
所有数据类型都继承自 Object
类。(包括int、String、函数以及 null 等等)。All is Object.
常用内置数据类型:
- num
- int
- double
- String
- bool (true | false)
- List ,数组
- Set,集合
- Map,字典
变量 & 常量
变量
dart 默认声明的为变量,变量声明主要有以下几种形式:
类型约定:
1
2
3
4String name; // 默认值为 null:未初始化的所有变量拥有一个默认的初始化值:null,包括 num。
name = 'Bob';
int age = 18;类型推断,通过 关键词
var
来声明1
2var name = 'Bob'; //name is String
name = 123; //编译报错使用动态类型,通过 关键词
dynamic
来声明, 类似 OC 中的id
1
2dynamic name = 'Bob'; //name is String
name = 123; //name is int
一般情况下,我们使用 var
来声明变量,让编译器帮我们推断其类型;
常量
常量声明涉及到两个关键字 final
和 const
。使用示例如下:
1 | final String name; // 编译报错:未初始化 |
- final 用于声明==运行时==常量;
- const 用于声明==编译时==常量;
示例说明如下:
1 | final abc = 1 + 2; //✅ |
函数
一般函数形式:
1 | bool isNoble(int atomicNumber) { |
函数参数分为两种:
必要参数
1
2
3bool isNoble(int atomicNumber/* 必要参数 */){
...
}可选参数:一个函数==仅可==包含一种可选参数
命名参数:
{type param1, type param2, ...}
1
2
3bool isNoble(int atomicNumber/* 必要参数 */, {bool bold = false, Widget child}){
...
}位置参数:
[type param1, type param2, ...]
,1
2
3bool isNoble(int atomicNumber/* 必要参数 */, [bool bold]){
...
}
函数注意点:
必要在前,可选在后;
可对参数设置默认值
isNoble(int atomicNumber = 110)
即使是可选参数,也可通过关键字
@required
将其约束为 必要的(调用函数时,必须对该参数赋值)函数也是对象,可作为参数传递
1
2
3
4
5
6
7
8void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// 将 printElement 函数作为参数传递。
list.forEach(printElement);
匿名函数
类似 OC 中的 Block
1 | void main() { |
表达式
表达式基本继承自类 C 语言,OC中没有的表达式,有如下几个:
~/
除并取整1
2print(5/2 == 2.5); //true, 此处不同于 OC ,返回整数
print(5 ~/ 2 == 2); //trueas
类型转换 |is
is!
类型判断1
2
3
4
5
6
7
8
9
10
11
12// 类型检查
if (emp is Person) {
emp.firstName = 'Bob';
}
// 类型检查
if (emp is! Person) {
//TODO:
}
//此时要确保 emp 是 Person 类型,否则会抛出异常
(emp as Person).firstName = 'Bob';..
级联 在 同一个对象 上连续 调用 多个对象的变量或方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15querySelector('#confirm') // 获取对象 (Get an object).
..text = 'Confirm' // 使用对象的成员 (Use its members).
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
//equal to
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
//返回值为 void 的方法则不能使用级联运算符
var sb = StringBuffer();
sb.write('foo')
..write('bar'); // 出错:void 对象中没有方法 write?? 空判断
expr1 ?? expr2
expr1 非空,则返回其值,否则执行 expr2 并返回值1
2
3
4var num;
print(num??123); // 123
num = 456;
print(num??123); // 456??=
空则赋值1
2
3
4
5
6
7var a = 123;
a ??= 456;
print(a); //123
var b;
b ??= 456;
print(b); //456?.
条件访问成员1
2
3
4List numbs;
print(numbs?.length); // null
numbs = [1, 2, 3];
print(numbs?.length); // 3
流程控制
流程控制语句也基本继承自 C 语言;按已有 OC 习惯使用即可;
异常
关键字:
throw
try
catch
finally
流程及使用习惯,类 Java,如下:
1 | void breedMoreLlamas(){ |
类
- 一个类只有一个父类;
- 类可扩展,同OC;
成员使用
1 | var p = Point(2, 2); |
构造函数
1 | class Point { |
常量构造函数
两个使用相同构造函数相同参数值构造的编译时常量是同一个对象:
1 | var a = const ImmutablePoint(1, 1); |
命名式构造函数
- 声明一个类多个命名式构造函数来表达更明确的意图;
- 不能被继承;
1 | class Point { |
初始化列表
除了调用父类构造函数之外,还可以在构造函数体执行之前初始化实例变量。每个实例变量之间使用逗号分隔。
1 | // Initializer list sets instance variables before |
其他构造函数
详情参见Dart API 文档:
重定向构造函数
:类似OC 中的 非指定构造函数工厂构造函数
:关键字factory
, 使用该构造函数构造类的实例时==并非==总是会返回新的实例对象。例如,工厂构造函数可能会从缓存中返回一个实例,或者返回一个子类型的实例。==工厂中不可以访问 this==
获取对象类型
关键字 runtimeType
1 | print('The type of a is ${a.runtimeType}'); |
方法 & 属性
使用同OC, 但使用 .
语法
每个属性都有 Getter
方法, 对于 非 final
属性,还有 Setter
方法。 可以通过 关键字 get
和 set
为额外的属性(如计算属性),添加 Getter 和 Setter 方法:
1 | class Rectangle { |
抽象类
使用关键字 abstract
标识类可以让该类成为 抽象类,抽象类将无法被实例化。抽象类常用于声明接口方法、有时也会有具体的方法实现。如果想让抽象类同时可被实例化,可以为其定义工厂构造函数。
1 | abstract class Doer { |
隐式接口
骚操作
接口 即 OC 中的协议。
==每一个类都隐式地定义了一个接口并实现了该接口,这个接口包含所有这个类的实例成员以及这个类所实现的其它接口。如果想要创建一个 A 类支持调用 B 类的 API 且不想继承 B 类,则可以实现 B 类的接口。==
一个类可以通过关键字 implements
来实现一个或多个接口并实现每个接口定义的 API:
1 | // Person 类的隐式接口中包含 greet() 方法。 |
实现多个接口,使用逗号分隔:
1 | class Point implements Comparable, Location {...} |
扩展
子类化
使用 extends
关键字来创建一个子类,并可使用 super
关键字引用一个父类:
1 | class TV{ |
重写
子类可以重写父类的实例方法、Getter 以及 Setter 方法。使用 @override
来标示:
1 | class SmartTV extends TV { |
扩展方法
一种向现有库添加功能的方式。extension extensionName on ClassName{...}
1 | extension NumberParsing on String { |
Mixin
一种在多重继承中复用某个类中代码的方法模式。
定义一个类继承自 Object 并且不为该类定义构造函数,这个类就是 Mixin 类,除非你想让该类与普通的类一样可以被正常地使用,否则可以使用关键字 mixin
替代 class
让其成为一个单纯的 Mixin 类。
1 | mixin Musical { |
使用 with
关键字并在其后跟上 Mixin 类的名字来使用 Mixin 模式:
1 | class Musician extends Performer with Musical { |
使用关键字 on
来指定哪些类可以使用该 Mixin 类,比如有 Mixin 类 A,但是 A 只能被 B 类使用,则可以这样定义 A:
1 | mixin MusicalPerformer on Musician { |
枚举
枚举具有较大的局限性,其值不可自定义。
每一个枚举值都有一个名为 index
成员变量的 Getter 方法,该方法将会返回以 0 为基准索引的位置值。例如,第一个枚举值的索引是 0 ,第二个枚举值的索引是 1。以此类推。
1 | enum Color { red, green, blue } |
类变量及类方法
使用 static
修饰
类成员
1 | class Queue { |
静态变量在其首次被使用的时候才被初始化。
类方法
1 | import 'dart:math'; |
泛型
如今的每个现代编程语言都支持泛型。日常使用中,最常见于集合。常用于需要要求类型安全的情况,好处还有:
- 适当地指定泛型可以更好地帮助代码生成。
- 使用泛型可以减少代码重复。
1 | var names = List<String>(); |
在上述代码中,为集合指定类型为 String,方便编译器检查,避免插入错误类型数据;
1 | abstract class Cache<T> { |
在上述代码中,T 是一个替代类型。其相当于类型占位符,在开发者调用该接口的时候会指定具体类型。
泛型约束
可能会想限制泛型的类型范围,这时候可以使用 extends
关键字:
1 | class Foo<T extends SomeBaseClass> { |
泛型方法
在方法中,使用泛型
1 | T first<T>(List<T> ts) { |
异步
不同于 OC 中 通过 GCD 和 NSOperation 来支持异步编程。
Dart 中 使用 Future
和 await/async
来支持异步编程。
Future
Future
基本使用上类似于 GCD,如下:
1 | void main() { |
其中Dart的事件循环,类似 OC 中的 Runloop, 大概如下:
1、Dart的入口是main函数,所以main函数中的代码
会优先执行;
2、main函数执行完后,会启动一个事件循环(Event Loop)就会启动,启动后开始执行队列中的任务;
3、首先,会按照先进先出的顺序,执行 微任务队列(Microtask Queue)
中的所有任务;
4、其次,会按照先进先出的顺序,执行 事件队列(Event Queue)
中的所有任务;
为了避免 地狱回调 的问题,dart 又通过引入 await/async
来实现以同步的代码风格,实现异步调用。
await/async
1 | Future<String> getNetworkData() async { |
其他
isolates
dart 中独有的概念,用于代替线程使用,详情参见官网;