Dart
基于2.7.2的版本
API参考[https://api.dart.dev/stable/2.8.0/index.html]
官方指导[https://dart.dev/guides/language/language-tour]
Built-in types
- numbers (int, double)
- strings
- booleans
- lists (also known as arrays)
- sets
- maps
- runes (for expressing Unicode characters in a string)
- symbols
- 一切皆对象, 默认值为null (eg.numbers)
int a = 1;
double b = 1.0;
num c = 1;
String d = 'string';
bool e = true;
List list = [1, 2, 3];
List
Function
main()
- Dart程序都是冲main()函数执行,
-
main()
返回void
, 有个可选的参数List
void main(List arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
简写
- 函数语句可以简写, 使用
=>
简写替代{ return expr; }
- 会自动推断函数返回值, 返回值可以省略, 但是会有警告
- 函数体中无
return
语句, 返回值为null
- 函数体中无
int calculate() => 6 * 7;
calculate2() => 6 * 7;
main() => print('calculate result: ${calculate()}');
可选命名参数, Named parameters
- 使用
{}
包裹 - 可以使用
@required
注解, 表示该参数一定要传入; 使用该注解, 需要依赖meta
包的package:meta/meta.dart
- 可以定义默认值, 不定义即为
null
- 调用时,使用
paramName:value
传入参数, 可以不按顺序
String sayHello(String from, {String msg}) {
return '${from} say hello, ${msg}';
}
void main(List arguments) {
print(sayHello('Bob'));
print(sayHello('Bob', msg: "I'm Bob ~"));
}
可选位置参数, Positional parameters
- 使用
[]
包裹 - 可以可以定义默认值, 不定义即为
null
- 调用时, 即使有默认值, 传入参数时, 也不能跳过
String say(String from, String msg, [String device='Mac']) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
函数也是对象
- 函数都是
Function
的子类, 也可以作为参数传递void main(List
arguments) { ['a', 'b', 'c'].forEach(print); }
匿名函数
- 使用
(parms){return expr;}
格式 - 简单函数体, 也可以使用
=>
简写
void testLambda(int fun(int a, int b)) {
print(fun(3, 6));
}
void main() {
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
}
可调用类 callable class
- 普通的class可以添加
call
方法, 作为callable class
当做一个函数, 直接调用
class PrintWrapper {
void call(var message) {
print(message.toString());
}
}
void main(List arguments) {
var printWrapper = PrintWrapper();
printWrapper("this is message");
}
操作符, operators
算数操作符
++var
, var--
, -expr
.....
- / Divide
- ~/ Divide, returning an integer result
- % Get the remainder of an integer division (modulo)
assert(5 / 2 == 2.5); // Result is a double
assert(5 ~/ 2 == 2); // Result is an int
assert(5 % 2 == 1); // Remainder
比较
==
, >
, !=
....
类型判断
as
, is
, is!
(emp as Person).firstName = 'Bob';
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
赋值
// Assign value to a
a = value;
// Assign value to b if b is null; otherwise, b stays the same
b ??= value;
条件语句
- 三元表达式
condition ? expr1 : expr2
-
expr1 ?? expr2
, 三元表达式判空简写, 和expr1 != null ? expr1 : expr2
作用相同
函数调用
-
?.
非空调用, 和kotlin一样 -
..
级联操作, 类似于kotlin中的apply
, 其中的this
即为前一个对象
querySelector('#confirm') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
操作符重载
class Point {
var x, y = 0;
Point(num x, num y) {
this.x = x;
this.y = y;
}
@override
bool operator ==(other) {
if (other is Point) {
return x == other.x && y == other.y;
}
return false;
}
Point operator +(Point p) {
return Point(x + p.x, y + p.y);
}
}
异常 try catch finally
-
throw
抛异常, 可以是任何对象throw FormatException('Expected at least 1 section'); throw 'Out of llamas!';
catch
捕获所有异常,on
捕获指定异常
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');
}
对象
变量
- 使用
_
开头, 代表私有变量 - 默认有
setter
,getter
方法,final
类型没有setter
, 私有变量没有gettter
- 有类型推断, 可以使用
var
声明-
var
是动态类型, 后续可以改变类型, 类似声明为Object
- 和声明成
Object
不同的是, 可以使用实例相应的方法, 而Object
需要做相应的类型转换 -
dynamic
和var
好像不区分了, 目前没找到相应的文档
-
- const为编译试常量, 在Class中必须声明成static
构造方法
- 未声明构造方法时, 提供不含任何参数的构造方法, 和Java类似
- 构造方法不可以重置, 即有且仅有一个构造方法
简写, 可以在构造方法中直接赋值
class Point {
num x, y;
Point(this.x, this.y);
}
Default const
- Initializer list
- 简写, 直接在方法体外面写赋值语句
- Named constructors
- 类似于Java中的
static
方法, 用于去创建对象, 感觉是对方法不可重载的折中
- 类似于Java中的
- Redirecting constructors
- 调用自身的其他构造方法
- Factory constructors
- 构造方法前, 使用
factory
关键字修饰 - 工厂方法的语法糖, 内部可以自己实现逻辑, eg.实现一个简单工厂
- 方法体类, 没有
this
指针, 需要主动构造此对象的实例并返回 - 属于构造方法, 由于构造方法不能重置,因此只能调用
Named constructors
生成自身实例
- 构造方法前, 使用
class Point {
var x, y = 0;
Point(num x, num y) {
this.x = x;
this.y = y;
}
// Initializer list
Point.fromJson(Map json)
: x = json['x'],
y = json['y']{}
// Named constructors
Point.origin() {
x = 0;
y = 0;
}
// Redirecting constructors
Point.originWithX(num x) : this(x, 0);
}
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to
// the _ in front of its name.
static final Map _cache =
{};
factory Logger(String name) {
return _cache.putIfAbsent(
name, () => Logger._internal(name));
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
封装、继承、多态
- Dart的函数, 不可重载
-
interface
是关键字, 没有单独的interface, 所有的class都是隐藏的interface- 类似Java中的接口, 可以使用
abstract classes
代替
- 类似Java中的接口, 可以使用
- 多重继承
mixins
, 使用extends
with
去继承- mixin的对象, 不能有构造方法
- 如果mixin的对象, 不打算作为普通的对象去使用, 定义时 可以使用
mixin
关键字代替class
异步
Futrue / 协程
-
创建
- 使用
async
声明一个异步调用的方法,async
方法返回的是一个Future
- 使用
Future
的工厂方法,delayed
,error
,value
...
- 使用
-
使用
- 在
async
方法中, 可以使用awite
获取另一个async
方法的返回值 -
Future
也可以使用then
方法回调消费数据, 详细参考API文档 -
Future
的静态方法,any()
,forEach
,wait
等可以对多个Future
进行操作
- 在
import 'dart:io';
Future getVersion() async {
sleep(Duration(seconds: 1));
return '2.7.2';
}
printVersion() async{
var version = await getVersion();
return print('version: $version');
}
main() {
getVersion().then((version) => print('then version: $version'));
printVersion();
print('----');
sleep(Duration(seconds: 5));
}
Stream
类似一个生产者/消费者; 上游生产数据, 下游消费, 并且有一次类似Rx的操作符(eg. map
, take
...), 用于下游做数据处理转换;
类似Rx,
- Observable
对应Stream
;
- ObservableEmitter
对应StreamSink
- Observable.subscribe()
方法对应Stream.listen()
- Disposable
对应StreamSubscription
; 不过StreamSubscription
支持数据流的pause
,resume
; 感觉融合的Rx的backpress
-
创建
- 使用Stream自身的工厂方法去创建, eg
Stream.empty()
,fromIterable
,fromFuture
- 使用
async*
方法创建, 使用yield
传递值 - 使用
StreamController
创建
- 使用Stream自身的工厂方法去创建, eg
-
消费
- 在
async
方法中, 使用await for(var in stream)
进行消费 - 使用
listen
方法
- 在
// Stream 工厂方法创建
Stream createStringStream() {
var list = ['a', 'b', 'c'];
return Stream.fromIterable(list);
}
// async* 创建Stream
Stream asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) {
await Future.delayed(Duration(seconds: 1));
yield k++;
}
}
// StreamController创建Stream
Stream timedCounter(Duration interval, [int maxCount]) {
StreamController streamController;
Timer timer;
int counter = 0;
void tick(_) {
counter++;
streamController.add(counter); // 使用add 发送一个数据
if (counter == maxCount) {
timer.cancel();
streamController.close(); // 关闭stream
}
}
void startTimer() {
timer = Timer.periodic(interval, tick);
}
void stopTimer() {
timer?.cancel();
timer = null;
}
streamController = StreamController(
onListen: startTimer,
onResume: startTimer,
onPause: stopTimer,
onCancel: stopTimer);
return streamController.stream;
}
main() async {
createStringStream().forEach(print);
asynchronousNaturalsTo(3).forEach(print);
var subsucription = timedCounter(Duration(seconds: 1)).listen(print);
Future.delayed(Duration(seconds: 3), () {
subsucription.cancel();
});
await for (var i in timedCounter(Duration(seconds:2), 5)){
print('await for stream: $i');
}
}