之前在学习flutter,本以为自己可以轻松上手掌握dart,结果发现经常有不懂的语法。所以决定踏踏实实的学习一遍dart。网上有很多相关学习资料.我主要从官网来学习,然后又找了一个视频来补充学习。
文章中涉及的代码,可以从我的Github上找到。
其实这样学下来的话,是比较耗时间的,但是我想还是扎实一下基础吧,磨刀不误砍柴工。往往有时候做项目的话,遇到一些细节,就会模棱两可,含糊不清。甚至有时候看到一块代码,并没有接触过,然后去百度、查文档,虽然当时有印象了,但知识并不系统。
我的电脑是Mac系统,所以需要安装Homebrew。这里我遇到的问题是始终下载失败。最后是通过科学上网以后才下载成功。
接着按照官网给出的示例,安装dart。
最后在终端中输入dart --version
显示dart版本号就说明安装成功了。
Dart环境我使用的是VS Code,非常简单,只需要安装Dart的插件即可。
编写一个测试文件:test.dart
void main(){ // dart类似java,需要一个入口main函数 print('123'); }
运行结果如上,说明Dart环境已经配置好了。
注意:
test_hello
,而不要使用驼峰命名了。可以去查看官方规范手册。变量定义
//变量仅存储对象的引用 var name = 'Bob'; // 未初始化的变量拥有一个默认的初始化值:null。即便数字也是如此,因为在 Dart 中一切皆为对象,数字也不例外。 int lineCount; assert(lineCount == null);
final和const
final name = 'Bob'; // Without a type annotation final String nickname = 'Bobby'; // const 关键字不仅仅可以用来定义常量,还可以用来创建 常量值 var foo = const []; final bar = const []; const baz = []; // 相当于 `const []` (Equivalent to `const []`) // 还可以在变量中使用类型检查 // Valid compile-time constants as of Dart 2.5. const Object i = 3; // Where i is a const Object with an int value... const list = [i as int]; // Use a typecast. const map = {if (i is int) i: "int"}; // Use is and collection if. const set = {if (list is List<int>) ...list}; // ...and a spread
Dart 两种数据类型:int
和double
下面是字符串和数字之间转换的方式:
// String -> int var one = int.parse('1'); assert(one == 1); // String -> double var onePointOne = double.parse('1.1'); assert(onePointOne == 1.1); // int -> String String oneAsString = 1.toString(); assert(oneAsString == '1'); // double -> String 并且保留了指定的小数 String piAsString = 3.14159.toStringAsFixed(2); assert(piAsString == '3.14');
使用单引号或双引号定义
var s1 = 'Single quotes work well for string literals.'; var s2 = "Double quotes work just as well."; var s3 = 'It\'s easy to escape the string delimiter.'; var s4 = "It's even easier to use the other delimiter.";
字符串拼接可以使用+
或者直接挨在一起的方式
var s1 = 'String ' 'concatenation' " works even over line breaks."; var s2 = 'The + operator ' + 'works, as well.';
可以使用三个单引号或者三个双引号创建多行字符串:
var s1 = ''' 你可以像这样创建多行字符串。 '''; var s2 = """这也是一个多行字符串。""";
只要是编译时常量都可以作为字符串字面量的插值表达式
void main(List<String> args) { // These work in a const string. const aConstNum = 0; const aConstBool = true; const aConstString = 'a constant string'; // These do NOT work in a const string. var aNum = 0; var aBool = true; var aString = 'a string'; const aConstList = [1, 2, 3]; const validConstString = '$aConstNum $aConstBool $aConstString'; print(validConstString); // 非编译时变量,不可以赋值 // const invalidConstString = '$aNum $aBool $aString $aConstList'; var invalidConstString = '$aNum $aBool $aString $aConstList'; // 编译时变量,可以赋值 }
string常用属性:
void main(List<String> args) { String a = '123'; print(a.length); // 3 print(a.isEmpty); // false print(a.isNotEmpty); // true }
string常用属性
布尔类型只有true
和false
。
void main(List<String> args) { var test; // dart中一切皆是对象,所以要显示判断是否为null if(test == null){ print('test is null'); } }
Dart中数组用List
对象表示。
void main(List<String> args) { // 1.可以混合的list var list1 = [1, 2, 3, '4']; print(list1); // 2.只可以是指定类型的list List<int> list2 = [1, 2, 3]; print(list2); // 3.用const关键字定义一个编译时数组常量 List<int> list3 = const [1, 2, 3]; print(list3); list3[1] = 4; // 编译时出错,不可以修改 // 4.通过构造方式创建数组 List fixedLengthList = new List(3); print(fixedLengthList.length); // 3 }
Dart中数组长度类似JavaScript语法。
void main(List<String> args) { var list = [1, 2, 3, 4]; print(list.length); // true print(list[1] == 2); // true list[2] = 4; print(list); // [1, 2, 4, 4] }
Dart中新增扩展操作符
void main(List<String> args) { var list1 = [1, 2, 3, 4, 5]; var nullList; // 使用 ... 扩展list插入到另一个list var list2 = [0, ...list1]; print(list2); // [0, 1, 2, 3, 4, 5] // 使用 ...? 如果nullList为空,则不插入 var list3 = [0, ...?nullList]; print(list3); }
Dart 还可以使用Collection If
和Collection for
来根据条件创建数组。
void main(List<String> args) { /// 可以根据test条件,动态创建数组 var test = true; var list = [ 1, 2, 3, if(test) 4 ]; print(list); // [1, 2, 3, 4] /// 也可以用循环遍历另一个数组创建一个数组 var arrays = [1,2,3,4]; var location = [ '#0', for(var i in arrays) '#$i' ]; print(location); // [#0, #1, #2, #3, #4] }
List常用的操作
void main(List<String> args) { var a = [1, 2, 3]; a.add(4); print(a); // [1, 2, 3, 4] a.insert(1, 100); print(a); // [1, 100, 2, 3, 4] a.remove(4); print(a); // [1, 100, 2, 3] // 打乱顺序 a.shuffle(); print(a); // [2, 3, 100, 1] print(a.asMap()); // {0: 1, 1: 100, 2: 2, 3: 3} // 排序 List<String> numbers = ['two', 'three', 'four']; // Sort from shortest to longest. numbers.sort((a, b) => a.length.compareTo(b.length)); print(numbers); // [two, four, three] // 截取 a.sublist(1); print(a); // 可以调用print直接打印,或者自定义其他函数 numbers.forEach(print); }
创建sets
void main(List<String> args) { var names = <String>{}; // 类型+{}的形式创建Set。 Set<String> names2 = {}; // 声明类型变量的形式创建 Set (This works, too). var names3 = {}; // 这样的形式将创建一个 Map 而不是 Set (Creates a map, not a print(names.runtimeType); // _CompactLinkedHashSet<String> print(names3.runtimeType); // _InternalLinkedHashMap<dynamic, dynamic> }
注意:
如果忘记在 {}
上注释类型或赋值到一个未声明类型的变量上,那么 Dart 会创建一个类型为 Map<dynamic, dynamic>
的对象。
创建Map
void main(List<String> args) { // 相当于 Map<String, String> test = {} var test = { 'a': '1', 'b': '2', 'c': '3' }; // 可以不使用关键字New实例化一个对象 var gifts = Map(); gifts['first'] = 'partridge'; gifts['second'] = 'turtledoves'; gifts['fifth'] = 'golden rings'; }
操作Map
void main(List<String> args) { var map = {'a': 1, 'b': 2, 'c': 3}; print(map.length); // 3 print(map.isNotEmpty); // true print(map.isEmpty); // false print(map.keys); // (a, b, c) print(map.values); // (1, 2, 3) print(map.containsKey('c')); // true print(map.containsValue(4)); // false // 移除 map.remove('a'); // {b: 2, c: 3} print(map); map.forEach((key, value) { print('key = $key, value = $value'); // key = b, value = 2 // key = c, value = 3 }); }
以前我没用过取整运算符,这里记一下。
void main(List<String> args) { print(2 + 3 == 5); print(2 - 3 == -1); print(2 * 3 == 6); // 除 print(5 / 2 == 2.5); // 结果是一个浮点数 // 取整 print(5 ~/ 2 == 2); // 结果是一个整数 // 取余 print(5 % 2 == 1); // 取余 print('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1'); }
自增与自减
文档中的自增与自减解释很棒。
void main(List<String> args) { var a, b; a = 0; b = ++a; // 在 b 赋值前将 a 增加 1。 print(a == b); // 1 == 1 a = 0; b = a++; // 在 b 赋值后将 a 增加 1。 print(a != b); // 1 != 0, a = 1 a = 0; b = --a; // 在 b 赋值前将 a 减少 1。 print(a == b); // -1 == -1, a = -1 a = 0; b = a--; // 在 b 赋值后将 a 减少 1。 print(a != b); // -1 != 0 }
void main(List<String> args) { print(2 == 2); print(2 != 3); print(3 > 2); print(2 < 3); print(3 >= 3); print(2 <= 3); }
当且仅当 obj
实现了 T
的接口,obj is T
才是 true。
void main(List<String> args) { Emp emp = Emp(); Person p = Person('张三'); print(emp is Person); // true } class Person{ final _name; Person(this._name); } class Emp implements Person{ // 必须实现 get _name => ''; }
=
来赋值??=
来为值为 null 的变量赋值void main(List<String> args) { var a; var b; a = 1; // 当且仅当 b 为 null 时才赋值 b ??= 2; print(a); // 1 print(b); // 2 var c = 9; c ~/= 2; print(c); // 4 }
使用逻辑运算符你可以反转或组合布尔表达式
void main(List<String> args) { var flag = true; const tab = 0; if (flag && (tab == 3 || tab == 0)) { print('hello'); // hello } }
?:
??
void main(List<String> args) { // 三目运算符写法 good String playName(String name) => name != null ? name : 'Tom'; // ??写法 best String playName3(String name) => name ?? 'Tom'; // if - else 写法 bad String playName2(String name) { if (name != null) { return name; } else { return 'Tom'; } } }
级联运算符(..)
可以让你在同一个对象上连续调用多个对象的变量或方法。
void main(List<String> args) { // 级联运算符严格意义上说并不是一个操作符,而是dart的特殊语法 var p = Person() ..name = 'tom' ..age = 1 ..say(); // name = tom, age = 1 // 最后直接调用了say方法 } class Person{ String name; int age; void say(){ print('name = $name, age = $age'); } }
与 JavaScript 不同的是,Dart 的 if 语句中的条件必须是一个布尔值,不能是其它类型
void main(List<String> args) { var bar = false; if (bar ==false){ print('false'); }else if( bar == true){ print('true'); }else{ print('not true or false'); } }
Dart在循环中的闭包会自动捕获。下面的例子在JavaScript中就会输出两个2。
void main(List<String> args) { // for 循环中的闭包会自动捕获循环的 索引值 以避免 JavaScript 中一些常见的陷阱 var list = []; for(var i =0; i<2; i++){ list.add(()=>print(i)); } list.forEach((v) => v()); }
不需要数组索引时,使用forEach即可
var prints = [1, 2,3]; prints.forEach((v)=>print(v));
List和Set支持for-in
// List和Set支持for-in var collections = [1, 2, 3, 4]; for (var i in collections) { print('i = $i'); print(i); }
void main(List<String> args) { var i = 0; while (true) { ++i; print(i); // 1 2 3 if (i == 3) break; } print('i = $i'); do { i++; print(i); // 4 5 6 if(i == 6)break; } while (true); }
void main(List<String> args) { for(var i = 0; i<3; i++){ print(i); // 输出0 1 if(i ==1 ){ // 跳出循环 break; } print('hi 我被执行了'); // 只输出一次 } for(var i = 0; i<3; i++){ print(i);// 输出 0 1 2 if(i ==1 ){ // 继续循环 continue; } print('hi 我被执行了'); // 输出2次,第二次被跳过了,循环继续 } }
case
如果为空,则采用fall-through
形式case
如果为非空,则采用continue
和label标签void main(List<String> args) { var name = 'annie'; switch (name) { case 'tim': print('tim'); break; case 'peter': print('peter'); break; case 'jack': // fall-through 形式 case 'tom': print('jack and tom'); break; case 'annie': print('annie'); continue ruth; // 继续执行标签为ruth的语句 ruth: case 'ruth': print('ruth'); break; case 'wilson': var test = 'test'; // 局部变量 print(test); break; } }
assert
是否生效依赖开发工具和使用的框架,在命令行中可以执行dart
命令void main(List<String> args) { var num = 100; // 命令行中执行 dart --enable-asserts 断言.dart // 然后就会报错,后面的所有内容不再执行 assert(num < 10); // 第二个参数可以指定异常错误信息 assert(num < 90, '异常: ($num) 不小于90'); // 如果直接执行 dart 断言.dart 因为是在生产环境,所以不会出现错误 }
void main(List<String> args) { // 函数最好定义返回值 bool isBool(bool flag){ var test = false; return test; } // 不写返回值倒是也行 isBool2(bool flag){ var test = false; return test; } // 使用箭头函数 isBool3(bool flag) => flag = false; }
虽然参数是可选,但是也可以指定某个参数为必传,使用@required
。
import 'package:meta/meta.dart'; void main(List<String> args) { // 定义一个可选命名参数 void saySomething({String name, int age}){ print('name = $name, age = $age'); } // 调用可选命名参数时,不需要写{} saySomething(name: 'tom', age: 12); // name = tom, age = 12 saySomething(name: 'cook'); // name = cook, age = null // time参数必须传递 // 使用@required 注解必须导入meta包 // 导入meta.dart包,则必须在pubspec.yaml 文件中进行声明 void playGame({String name,@required int time}){ print('name = $name, age = $time'); } // 虽然使用了@required 注解,并不会对应用程序报错,而是发出警告 playGame(name: '和平精英'); }
位置可选参数用 []
表示
void main(List<String> args) { void say(String name, int age, [String address]){ if(address == null){ print('name = $name, age = $age'); }else{ print('name = $name, age = $age, address = $address'); } } say('tom', 123); // name = tom, age = 123 say('tim', 34, '北京'); // name = tim, age = 34, address = 北京 }
可以使用=
给可选参数设置默认值
void main(List<String> args) { void startEng({bool oil = true, bool state = false}){ return print(oil && state); } startEng(); // 默认false startEng(state:true); // true startEng(oil:true,state:true); // true // 如果name使用默认值,但是传递 age 呢? String say(String start, String end, [String name = 'jack', int age]){ if(name != null){ // 永远不为null print('start = $start, end = $end, name = $name'); } if(age!=null){ print('start = $start, end = $end, name = $name, age = $age'); } } say('北京','上海', '张三'); say('河南','河北', 'jack', 22); }
main
函数void main(List<String> args) { print(args); // 命令行中没有传递参数时 if(args.length == 0)return; // 命令行中执行 dart main函数.dart 1 test if(int.parse(args[0]) == 1){ print('第一个参数为 ${args[0]}'); } if(args.length == 2){ print('参数的个数是 ${args.length}'); } }
void main(List<String> args) { void say(int num){ print('hello dart, and num is $num'); } List<int> list = const [1,2,3,4]; // 将函数作为参数传递给另一个函数 list.forEach(say); // 将函数作为一个变量 var printName = (v)=>print('you are print $v'); printName('jack'); // you are print jack var printName2 = (v){ return print('another print name function $v');}; printName2('tom'); // another print name function tom }
void main(List<String> args) { var list = [1, 2, 3, 4]; // 匿名函数 list.forEach((v) { print(v); }); // 箭头函数 list.forEach((v) => print(v)); List<String> getTime(List list, String times(str)) { List<String> tmp = []; list.forEach((v) { tmp.add(times(v)); }); return tmp; } String times(str) { return str * 3; } var list2 = ['h', 'e', 'l', 'l', 'o']; // 这里调用 times 时不需要写(),否则就变成了执行函数了 print(getTime(list2, times)); // [hhh, eee, lll, lll, ooo] }
bool topLevel = true; void main() { var insideMain = true; void myFunction() { var insideFunction = true; // 内部函数可以逐级向上访问外部函数变量 void nestedFunction() { var insideNestedFunction = true; print(topLevel); print(insideMain); print(insideFunction); print(insideNestedFunction); } } }
void main(List<String> args) { // 闭包就是一个函数对象 // 函数可以封闭它作用域内的变量,即使是函数在外部调用 Function sum(int add){ // 注意返回值类型是函数 return (i) => add + i; } // 这个1就是add,然后被封闭了起来 var sumAll = sum(1); print(sumAll(1)); // 2 // 闭包就是在一个函数中返回另一个函数 a(){ var count = 0; void printCount(){ print(count ++); } return printCount; } var fun = a(); // 想访问方法中的局部变量时,就使用闭包 fun(); // 0 fun(); // 1 }
void foo() {} // 定义顶层函数 (A top-level function) class A { static void bar() {} // 定义静态方法 void baz() {} // 定义实例方法 } void main() { var x; // 比较顶层函数是否相等。 x = foo; assert(foo == x); // 比较静态方法是否相等。 x = A.bar; assert(A.bar == x); // 比较实例方法是否相等。 var v = A(); // A 的实例 #1 var w = A(); // A 的实例 #2 var y = w; x = w.baz; // 这两个闭包引用了相同的实例对象,因此它们相等。 assert(y.baz == x); // 这两个闭包引用了不同的实例对象,因此它们不相等。 assert(v.baz != w.baz); }
void main(List<String> args) { // 这里没有指定函数返回值 // 实际上,如果指定了,编辑器就会报错 foo(){} var test = foo(); print(test); // null }
import
关键字导入dart:xxxx
package:xxxx
如果两个库代码有冲突,可以指定库前缀
import 'package:lib1/lib1.dart'; import 'package:lib2/lib2.dart' as lib2; // 使用 lib1 的 Element 类。 Element element1 = Element(); // 使用 lib2 的 Element 类。 lib2.Element element2 = lib2.Element();
只想使用代码库中的一部分,你可以有选择地导入代码库
// 只导入 lib1 中的 foo。(Import only foo). import 'package:lib1/lib1.dart' show foo; // 导入 lib2 中除了 foo 外的所有。 import 'package:lib2/lib2.dart' hide foo;
单行注释以 //
开始。所有在 //
和该行结尾之间的内容被编译器忽略。
// 单行注释
/*
开始,以 */
结尾。所有在 /*
和 */
之间的内容被编译器忽略void main() { /* * This is a lot of work. Consider raising chickens. Llama larry = Llama(); larry.feed(); larry.exercise(); larry.clean(); */ }
/// A domesticated South American camelid (Lama glama). /// /// Andean cultures have used llamas as meat and pack /// animals since pre-Hispanic times. class Llama { String name; /// Feeds your llama [Food]. /// /// The typical llama eats one bale of hay per week. void feed(Food food) { // ... } /// Exercises your llama with an [activity] for /// [timeLimit] minutes. void exercise(Activity activity, int timeLimit) { // ... } }
在生成的文档中,[Food] 会成为一个链接,指向 Food 类的 API 文档。
也就是说,在生成的文档中[Food]这个标识符就可以显示一个链接。
// 自定义一个类型 typedef Compare = int Function(Object a, Object b); /// 使用类型定义的情况 class SortedCollection { Compare compare; // 自定义类型 SortedCollection(this.compare); } // 简单的不完整实现。 int sort(Object a, Object b) => 0; void main() { SortedCollection coll = SortedCollection(sort); print(coll.compare is Function); // true print(coll.compare is Compare); // true }
(?.)
避免表达式为nullvoid main(List<String> args) { Person p = Person(); p.name = 'tom'; p.age = 12; print(p.name); // tom /// ?. // 因为p2是null,所以无法设置并且打印 // 但是使用了?.以后就不会报错了。 Person p2; p2?.name = 'jack'; p2?.age = 13; print(p2?.name); // null } class Person{ String name; int age; }
void main(List<String> args) { // 通过 类 创建实例 Person p = Person('tom', 12); print(p.name); // tom print(p.age); // 12 // 通过 类名.标识符 创建实例 Person p2 = Person.fromJson({'name': 'jack', 'age': 13}) ; print(p2.name); // jack print(p2.age); // 13 Animal a = const Animal('titi', 2); Animal b = const Animal('titi', 2); print(a.name); print(a.age); print(b.name); // 两个实例相等 print(identical(a,b)); // true } class Person{ String name; int age; Person(this.name, this.age); Person.fromJson(Map<String, dynamic> json){ name = json['name']; age = json['age']; } } // 常量构造函数 class Animal{ final String name; final int age; const Animal(this.name, this.age); }
void main(List<String> args) { Point p = Point(); print(p.x); // 调用x的 Getter p.y = 1; // 调用y的 Setter print(p.y); // 调用y的 Getter } class Point{ int x,y; }
void main(List<String> args) { Point p = Point.origin(); print(p.x); // 0 print(p.y); // 1 } class Point{ int x,y; Point(this.x, this.y); // 命名式构造函数 Point.origin(){ x = 0; y = 1; } }
调用顺序
传递给父类构造函数的参数不能使用 this 关键字。
使用(:)为子类的构造函数指定一个父类的构造函数。
class Person { String firstName; Person.fromJson(Map data) { print('in Person'); } } class Employee extends Person {. // Person没有默认构造函数 // 需要通过 super.fromJson 来显示调用 Employee.fromJson(Map data) : super.fromJson(data) { print('in Employee'); } } main() { var emp = new Employee.fromJson({}); // 打印: // in Person 先执行父类的构造 // in Employee if (emp is Person) { // emp类继承了Person emp.firstName = 'Bob'; } print(emp.firstName); // Bob (emp as Person).firstName = 'Jack'; print(emp.firstName); // Jack }
class Person { String firstName; // 初始化列表 会比 构造函数优先执行 Person.fromJson(Map data): firstName = data['firstName'] { print(firstName); } } main() { Person p = Person.fromJson({ 'firstName': 'zhangsan'}); }
设置final 字段
import 'dart:math'; class Point { final num x; final num y; final num distanceFromOrigin; // 初始化列表设置final属性,非常好用 Point(x, y) : x = x, y = y, distanceFromOrigin = sqrt(x * x + y * y); } main() { var p = new Point(2, 3); print(p.distanceFromOrigin); // 3.605551275463989 }
void main(List<String> args) {} class Point { int x, y; Point(this.x, this.y); // 重定向构造函数 // 在函数中调用另一个构造函数的形式 Point.origin(int num) : this(num, 0); }
void main(List<String> args) { Point p = const Point(0, 0); Point p2 = const Point(0, 0); Point p3 = Point(0, 0); // 这两个实例对象是相同的 print(identical(p, p2)); // true // 如果不使用const声明实例,则不会相等 print(identical(p, p3)); // false } class Point { // 变量必须用final 定义 final num x, y; const Point(this.x, this.y); // 构造函数也是常量 }
void main(List<String> args) { Person p = Person('tom'); p.say(); // tom } class Person{ String name; // 必须static 定义 static final Map<String, dynamic> _cach = Map<String, dynamic>(); factory Person(String name){ return _cach.putIfAbsent(name, () => Person._init(name)); } Person._init(this.name); void say(){ print(name); } }
对象的实例方法可以访问实例变量和this
。
void main(List<String> args) { Person p = Person('tom', 'hello title'); p.say(); } class Person{ String name; String title; Person(this.name, this.title); void say(){ // 可以访问变量 print('name is $name'); // 也可以访问this print(this.name); } }
你可以使用 get 和 set 关键字为额外的属性添加 Getter 和 Setter 方法
void main(List<String> args) { Point p = Point(1, 2, 3); print(p.point); // 6 p.point = 0; print(p.point); print(p.z); } class Point { int x, y, z; Point(this.x, this.y, this.z); get point => x + y + z; // TODO: 这里为啥设置point 却返回z的值? set point(int num) => z = num + x; }
void main(List<String> args) { } // 定义抽象类 abstract class Person{ // 定义抽象方法 void doSomething(); } class Zhangsan extends Person{ // 实现具体的方法 void doSomething(){ } }
void main(List<String> args) { var me = Me(); me.sayHello(); } abstract class Person{ String name; int age; void sayHello(); } class Me extends Person{ void sayHello(){ print('hello'); } }
一个类可以通过关键字 implements 来实现一个或多个接口并实现每个接口定义的 API。
void main(List<String> args) { print(saySomething(Person('张三'))); print(saySomething(Man())); } String saySomething(Person person) => person.sayName('李四'); class Person { String _name; Person(this._name); String sayName(String name) => '$_name,你好。我是$name'; } class Man implements Person { get _name => '谁也不是'; set _name(String name) => ''; // 因为存在隐式的setter,所以这个也要定义 String sayName(String name) => '$_name,你好。我是$name'; }
void main(List<String> args) { Man man = Man(); man.sayName(); } class Person{ void sayName() => print('hello person'); } class Man extends Person{ void sayName() => super.sayName(); // 调用父类方法 }
重写类成员
void main(List<String> args) { Man man = Man(); man.sayName(); } class Person{ void sayName() => print('hello person'); } class Man extends Person{ @override void sayName() => print('hello man'); // 重写实例方法 }
重写运算符
class Vector { final int x, y; Vector(this.x, this.y); Vector operator +(Vector v) => Vector(x + v.x, y + v.y); Vector operator -(Vector v) => Vector(x - v.x, y - v.y); // 运算符 == 和 hashCode 的实现未在这里展示,详情请查看下方说明。 // ··· } void main() { final v = Vector(2, 3); final w = Vector(2, 2); assert(v + w == Vector(4, 5)); assert(v - w == Vector(0, 1)); }
noSuchMethod
这个地方没有看明白
void main(List<String> args) { Man man = Man(); // man.name; // todo 怎么使用?? } class Person { void sayName() => print('hello person'); } class Man extends Person { void sayName() => super.sayName(); // 调用父类方法 @override void noSuchMethod(Invocation invocation) { print('你尝试使用一个不存在的成员:' + '${invocation.memberName}'); } }
使用枚举
void main(List<String> args) { print(Color.blue); // 获取枚举 print(Color.red.index); // 获取枚举下标 List<Color> colors = Color.values; // 获取全部枚举 print(colors[2]); } enum Color{ // 定义枚举 red, blue, green }
switch枚举
void main(List<String> args) { var aColor = Color.red; // 如果使用switch 则枚举中的每一个成员都得用case判断 // 否则就会发出警告 switch (aColor) { case Color.red: print('红色'); break; case Color.blue: print('蓝色'); break; case Color.green: print('绿色'); break; } } enum Color { // 定义枚举 red, blue, green }
覆写操作符基本格式:
返回类型 operator 操作符(参数1,参数2...){ 实现体... return 返回值 }
void main(List<String> args) { Musical musical = Musical(); musical.doSomethin(); } mixin Person { bool canCook = true; bool canSay = false; // mixin 模式不可以定义构造函数 // Person(); void doSomethin() { if (canCook == true) { print('可以做饭'); } else if (canSay == true) { print('可以说话'); } } } class Musical with Person{ @override void doSomethin() { // TODO: implement doSomethin super.doSomethin(); // 直接调用父类 print('我是子类哦'); } }
静态变量
void main(List<String> args) { print(Person.name); // test static } class Person{ static final String name = 'test static'; }
静态方法
import 'dart:math'; class Point { num x, y; Point(this.x, this.y); static num distanceBetween(Point a, Point b) { var dx = a.x - b.x; var dy = a.y - b.y; return sqrt(dx * dx + dy * dy); } } void main() { var a = Point(2, 2); var b = Point(4, 4); // 对于一些通用或常用的静态方法,应该将其定义为顶级函数而非静态方法。 var distance = Point.distanceBetween(a, b); assert(2.8 < distance && distance < 2.9); print(distance); }
代码错误提示
void main(List<String> args) { var names = List<String>(); // 声明为字符串数组,一旦不是则报错 names.addAll(['Seth', 'Kathy', 'Lars']); // 提示报错 // names.add(42); // Error }
减少重复代码
使用泛型声明一个类,让不同类型的缓存实现该类做出不同的具体实现。
void main(List<String> args) {} abstract class Cache<T> { T getByKey(String key); void setByKey(String key, T value); } class Acache extends Cache<String> { String getByKey(String key) { // 具体实现时指定 return 'hello'; } void setByKey(String key, String value) { // 具体实现时指定 print(11); } }
void main(List<String> args) { List list = <String>['1', '2', '3']; // 字符串集合 Set set = <String>{'1','2','3'}; // 字符串集合 Map map = <String, int>{'age': 1, 'size':12}; // Map }
void main(List<String> args) { // 与字面量相对应,也可以通过构造函数的方式使用泛型 Map map = Map<String, int>(); }
void main(List<String> args) { List list = List<String>(); // list.addAll(['1','2']); // 如果此时使用addAll则会报错 list.add('1'); list.add('2'); print(list is List<String>); // true var names = List<String>(); names.addAll(['小芸', '小芳', '小民']); print(names is List<String>); // true }
void main(List<String> args) { var someBaseClassFoo = Foo<SomeBaseClass>(); var extenderFoo = Foo<Extender>(); print(someBaseClassFoo.toString()); // 'Foo<SomeBaseClass>' 的实例 print(extenderFoo.toString()); // 'Foo<Extender>' 的实例 // 如果不指定泛型,默认是SomeBaseClass var foo = Foo(); print(foo); // 将非 SomeBaseClass 的类型作为泛型参数则会导致编译错误 // var foo = Foo<Object>(); } class SomeBaseClass {} // 这里的T,其实可以随意指定。一般是T、E、S、K等 class Foo<T extends SomeBaseClass> { // 具体实现…… String toString() => "'Foo<$T>' 的实例"; } class Extender extends SomeBaseClass {}
void main(List<String> args) { var list = List<String>(); list.addAll(['1','2']); var firstValue = first(list); print(firstValue); // 1 } T first<T>(List<T> ts) { // 处理一些初始化工作或错误检测…… T tmp = ts[0]; // 处理一些额外的检查…… return tmp; }
void main(List<String> args) {} // async 与 await同时使用 Future checkVersion() async { // 通过 try-catch 捕获异常 try { var version = await lookUpVersion(); } catch (e) { // 无法找到版本时做出的反应 } } void lookUpVersion() {}
void main(List<String> args) { } // 普通函数直接添加async关键字即可 Future<String> lookUpVersion() async => '1.0.0';
通过实现类的 call() 方法,允许使用类似函数调用的方式来使用该类的实例。
// WannabeFunction 类定义了一个 call() 函数,函数接受三个字符串参数,函数体将三个字符串拼接,字符串间用空格分割,并在结尾附加了一个感叹号 class WannabeFunction { String call(String a, String b, String c) => '$a $b $c!'; } var wf = WannabeFunction(); var out = wf('Hi', 'there,', 'gang'); main() => print(out);
制作的思维导图,加深学习印象。如有错误欢迎指正。
原始图片比较大,为了保证打开速度只上传了一张截图。如果需要高清图片可以在我的源码文件中找到。
参考资料: