1.Dart独特语法(相比于Java,C++,js)
1.1表达式
空安全
//表示允许为null String? name //如果fooList不为空,调用下标1的元素,否则为null fooList?[1] //如果foo不为空,调用foo的属性bar,否则 foo?.bar的值为null foo?.bar
修饰符 late
声明一个非空变量,但不在声明时初始化。
延迟初始化一个变量。
//懒加载初始化,直到temperature被调用,函数readThermometer()才会被调用,否则不调用 late String temperature = readThermometer();
运行时类型检查运算符 as is is!
//类型推断 (employee as Person).firstName = 'Bob'; if (employee is Person) { // Type check employee.firstName = 'Bob'; }
运算符 ??=
// 赋值给a a = value; // b为null时,需要使用??= ,否则b保持不变 b ??= value;
运算符 ??
//name如果不是null,则返回name的值,否则返回'Guest' String playerName(String? name) => name ?? 'Guest';
级联表示法
//下面的..等效于 paint. var paint = Paint() ..color = Colors.black ..strokeCap = StrokeCap.round ..strokeWidth = 5.0; //在第一个级联的前面加问号,相当于后面所有都有了判空的功能 querySelector('#confirm') // 获取对象 ?..text = 'Confirm' // 如果对象非空,则赋值 ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!')) ..scrollIntoView(); //嵌套级联 final addressBook = (AddressBookBuilder() ..name = 'jenny' ..email = 'jenny@example.com' ..phone = (PhoneNumberBuilder() ..number = '415-555-0100' ..label = 'home') .build()) .build();
文档注释
注解
库的导入
//两包里都有Element类,防止混淆 import 'package:lib1/lib1.dart'; import 'package:lib2/lib2.dart' as lib2; //使用lib1中的Element Element element1 = Element(); //使用lib2中的Element lib2.Element element2 = lib2.Element(); //只导入foo对象 import 'package:lib1/lib1.dart' show foo; //除了foo都导入 import 'package:lib2/lib2.dart' hide foo; /** 懒加载库,只有用到的时候才导入 减少网络应用程序的初始启动时间。 执行 A/B 测试——例如,尝试算法的替代实现。 加载很少使用的功能,例如可选屏幕和对话框。 */ import 'package:greetings/hello.dart' deferred as hello; Future<void> greet() async { //loadLibrary()可多次使用,只加载一次 await hello.loadLibrary(); hello.printGreeting(); }
1.2类型
Dart比java更加面向对象,基本类型,函数皆为对象。
新奇的基本类型
- Runes (
Runes
; often replaced by thecharacters
API)处理表情符号的用法,过于抽象,不想解释
- Symbols (
Symbol
)标识符用法
- The value
null
(Null
)空类型
- Records (
(value1, value2)
)记录类型
//record表达式是以逗号分隔的命名或位置字段列表,括在括号中: var record = ('first', a: 2, b: true, 'last'); //定义参数类型和返回类型 (int, int) swap((int, int) record) { var (a, b) = record; return (b, a); } // record类型的变量声明 (String, int) record; // 用record表达式初始化 record = ('A string', 123); var record = ('first', a: 2, b: true, 'last'); // $<position>来获取指定位置元素 print(record.$1); // 打印 'first' print(record.a); // 打印 2 print(record.b); // 打印 true print(record.$2); // 打印 'last' (num, Object) pair = (42, 'a'); var first = pair.$1; // Static type `num`, runtime type `int`. var second = pair.$2; // Static type `Object`, runtime type `String`. /* 相等性 */ (int x, int y, int z) point = (1, 2, 3); (int r, int g, int b) color = (1, 2, 3); print(point == color); // 打印 'true'. ({int x, int y, int z}) point = (x: 1, y: 2, z: 3); ({int r, int g, int b}) color = (r: 1, g: 2, b: 3); print(point == color); // 打印 'false'.类型不对应 /* 多重返回值 */ // 用record返回多重值 (String name, int age) userInfo(Map<String, dynamic> json) { return (json['name'] as String, json['age'] as int); } // 使用指定字段的record进行解构 final json = <String, dynamic>{ 'name': 'Dash', 'age': 10, 'color': 'blue', }; var (name, age) = userInfo(json); /* 等效于 var info = userInfo(json); var name = info.$1; var age = info.$2; */ ({String name, int age}) userInfo(Map<String, dynamic> json) // 用命名字段的record解构 final (:name, :age) = userInfo(json);
集合
//通过...将一个集合插入,此时list2元素有4个 var list = [1, 2, 3]; var list2 = [0, ...list]; assert(list2.length == 4); //list如果可能为null,可用...?预防 var list2 = [0, ...?list]; assert(list2.length == 1); //创建的值可能是三个也可能是四个,根据if来判断是否创建第四个 var nav = ['Home', 'Furniture', 'Plants', if (promoActive) 'Outlet']; var nav = ['Home', 'Furniture', 'Plants', if (login case 'Manager') 'Inventory']; //for创建 var listOfInts = [1, 2, 3]; var listOfStrings = ['#0', for (var i in listOfInts) '#$i']; assert(listOfStrings[1] == '#1'); var [a, b, ..., c, d] = [1, 2, 3, 4, 5, 6, 7]; // Prints "1 2 6 7". print('$a $b $c $d'); //rest表示剩余元素集合 var [a, b, ...rest, c, d] = [1, 2, 3, 4, 5, 6, 7]; // Prints "1 2 [3, 4, 5] 6 7". print('$a $b $rest $c $d');
函数
/// 设置参数默认值{} void enableFlags({bool bold = false, bool hidden = false}) {...} // bold值是true,hidden默认为false enableFlags(bold: true); //required表示参数必须提供,否则报错 const Scrollbar({super.key, required Widget child}); //用[]设置可选参数,如果不提供一个默认的值,则它们类型必须是可空且值为空 String say(String from, String msg, [String? device]) { var result = '$from says $msg'; if (device != null) { result = '$result with a $device'; } return result; } //如果不设置可为空,必须初始化 String say(String from, String msg, [String device = 'carrier pigeon']) { var result = '$from says $msg with a $device'; return result; } assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon'); //将函数作为参数 void printElement(int element) { print(element); } var list = [1, 2, 3]; // Pass printElement as a parameter. list.forEach(printElement); //将函数传给变量 var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!'; assert(loudify('hello') == '!!! HELLO !!!'); //匿名方法 const list = ['apples', 'bananas', 'oranges']; list .map((item) => item.toUpperCase()) .forEach((item) => print('$item: ${item.length}')); /*词法闭包 捕获外部变量:闭包可以捕获并保持其外部作用域(即词法作用域)中的变量。 延长变量生命周期:闭包能够延长被捕获变量的生命周期,使这些变量在闭包存在期间不会被回收。 函数与环境绑定:闭包是函数和它的词法环境的组合,闭包中保存的变量是函数定义时的变量,而不是函数调用时的变量。 */ /// 高阶函数(作用域在顶层),实现闭包 Function makeAdder(int addBy) { return (int i) => addBy + i; } void main() { // 创建一个加2的函数 var add2 = makeAdder(2); // 创建一个加4的函数 var add4 = makeAdder(4); assert(add2(3) == 5); assert(add4(3) == 7); } //没有返回值的函数默认返回null foo() {} assert(foo() == null); /* 生成器:延迟生成一系列值, 同步生成器: 返回一个Iterable对象. 异步生成器: 返回一个Stream对象. */ ///要实现同步生成器函数,请将函数体标记为sync*,并使用yield语句传递值: Iterable<int> naturalsTo(int n) sync* { int k = 0; while (k < n) yield k++; } //要实现异步生成器函数,请将函数体标记为async*,并使用yield语句传递值: Stream<int> asynchronousNaturalsTo(int n) async* { int k = 0; while (k < n) yield k++; } //通过使用yield*来提高递归生成器的性能: Iterable<int> naturalsDownFrom(int n) sync* { if (n > 0) { yield n; yield* naturalsDownFrom(n - 1); } } //声明与结构体实现分离的做法 external void someFunc(int i);
控制流
//词法闭包捕获下标 var callbacks = []; for (var i = 0; i < 2; i++) { callbacks.add(() => print(i)); } for (final c in callbacks) {//输出0,1 c(); } //遍历可迭代集合(list和set) for (final candidate in candidates) { candidate.interview(); } //处理从iterable中获取的值 for (final Candidate(:name, :yearsExperience) in candidates) { print('$name has $yearsExperience of experience.'); } //可迭代集合有forEach方法 var collection = [1, 2, 3]; collection.forEach(print); // 1 2 3 for (int i = 0; i < candidates.length; i++) { var candidate = candidates[i]; if (candidate.yearsExperience < 5) { continue; } candidate.interview(); } //可迭代集合能更简便替换上面这段 candidates .where((c) => c.yearsExperience >= 5) .forEach((c) => c.interview()); //switch case重写 // Where slash, star, comma, semicolon, etc., are constant variables... switch (charCode) { case slash || star || plus || minus: // Logical-or pattern token = operator(charCode); case comma || semicolon: // Logical-or pattern token = punctuation(charCode); case >= digit0 && <= digit9: // Relational and logical-and patterns token = number(); default: throw FormatException('Invalid'); } //改写为表达式 token = switch (charCode) { slash || star || plus || minus => operator(charCode), comma || semicolon => punctuation(charCode), >= digit0 && <= digit9 => number(), _ => throw FormatException('Invalid') }; /* 类的匹配 */ sealed class Shape {} class Square implements Shape { final double length; Square(this.length); } class Circle implements Shape { final double radius; Circle(this.radius); } double calculateArea(Shape shape) => switch (shape) { Square(length: var l) => l * l, Circle(radius: var r) => math.pi * r * r }; //Guard clause:通过when关键字满足一些特定条件 // Switch statement: switch (something) { case somePattern when some || boolean || expression: // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Guard clause. body; } // Switch expression: var value = switch (something) { somePattern when some || boolean || expression => body, // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Guard clause. } // If-case statement: if (something case somePattern when some || boolean || expression) { // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Guard clause. body; }
错误处理
//抛出给上层 throw FormatException('Expected at least 1 section'); //用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'); } try { breedMoreLlamas(); } catch (e) { print('Error: $e'); // Handle the exception first. } finally { cleanLlamaStalls(); // Then clean up. }