前文链接:
内容:
- 函数(方法)
 函数定义及各类函数;函数参数;闭包
- 面向对象
 定义;构造函数;成员(变量与函数)
 继承与多态;抽象类;接口;枚举类
 Mixins;操作符
- 泛型
 定义;用法;限制泛型类型
- 库和可见性
- 异常
- 元数据
五、函数(方法)
1、说明:
- Dart 是一个真正的面向对象语言,方法也是对象并且具有一种类型, - Function。
- 这意味着,方法可以赋值给变量,也可以当做其他方法的参数。也可以把 Dart 类的实例当做方法来调用。 详情请参考 Callable classes。 
- 方法都有返回值,当没有指定返回值的时候,函数返回null,即最后默认执行一句 - return null,可以省略不写。
2、函数定义及各类函数:
- 基本形式: - 返回值 方法名(参数1, 参数2, ...) { 方法体 return 返回值; }- 示例: - int sum(int a, int b) { return a + b; }
- 省略模式: 
 1、定义方法的返回值类型 和 参数 都可以省略- sum(a, b) { return a + b; } //sumResult = 9 print("sumResult = ${sum(3, 6)}");- 说明:建议明确方法(函数)的输入类型和返回值类型,既便于修改,也方便阅读。重要的是,如果不写方法参数输入类型,则在调用的时候,调用者可能无法明确参数类型(经测试,在编译阶段并未有相应的参数类型的检查提示),这就可能导致 - Unhandled exception之类的错误。
- 箭头函数: 
 1、语法:- => expr
 2、是- { return expr; }形式的缩写。- =>形式 有时候也称之为 胖箭头 语法。
 注:只适用于- 一个表达式的方法。
 3、示例:- int sum(int a, int b) => a + b;
- 匿名函数: 
 1、没有名字的函数,称之为- 匿名函数,有时候也被称为- lambda或者- closure 闭包。 2、你可以把匿名函数赋值给一个变量, 然后你可以通过这个变量使用这个函数。
 3、匿名函数和命名函数看起来类似,在括号之间可以定义一些参数,参数使用逗号分割,也可以是可选参数,大括号中的代码为函数体。- 定义: - ([[Type] param1[, …]]) { codeBlock; };
- 示例:``` Function sum = (int a, int b) - { return a + b; };- //sum = 9 print("sum = ${sum(3, 6)}"); - var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums']; //其中forEach接收一个函数 list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i)); ``` 
 
- 入口函数: 
 1、每个应用都需要有个顶级的- main()入口方法才能执行。- main()方法的返回值为- void并且有个可选的- List<String>参数。- void main(List<String> args) { print(arguments); }- 通过命令行可以将参数打印出来:  - 入口函数调用 
说明:其中使用`dart` 命令调用,参数用**空格**隔开- 函数别名 
 1、在 Dart 语言中,方法也是对象。
 2、使用- typedef, 或者- function-type alias来为方法类型命名, 然后可以使用命名的方法。
 3、当把方法类型赋值给一个变量的时候,- typedef保留类型信息。- 看下面一个简单的例子:
 - typedef int compare(int a, int b); int sort(int a, int b) { return a - b; } void main(List<String> args) { //(int, int) => int print(compare); //Closure: (int, int) => int from Function 'sort': static. print(sort); //true print(sort is Function); //true print(sort is compare); }- 说明:通过 - is操作符可以判断两个对象是否相等。- 看下面的例子:
 下面的代码没有使用 typedef:
 - class SortedCollection { Function compare; SortedCollection(int f(Object a, Object b)) { compare = f; } } // Initial, broken implementation. int sort(Object a, Object b) => 0; main() { SortedCollection coll = new SortedCollection(sort); // 我们只知道 compare 是一个 Function 类型, // 但是不知道具体是何种 Function 类型? assert(coll.compare is Function); }- 说明:当把 - f赋值给- compare的时候, 类型信息丢失了。- f的类型是- (Object, Object) → int(这里 → 代表返回值类型), 当然该类型是一个- Function。 如果我们使用显式的名字并保留类型信息, 开发者和工具可以使用 这些信息:- typedef int Compare(Object a, Object b); class SortedCollection { Compare compare; SortedCollection(this.compare); } // Initial, broken implementation. int sort(Object a, Object b) => 0; main() { SortedCollection coll = new SortedCollection(sort); assert(coll.compare is Function); assert(coll.compare is Compare); }- 注意: 目前,typedefs 只能使用在 function 类型上,但是将来 可能会有变化。
 
3、函数参数
- 静态作用域 
 1、Dart 是静态作用域语言,变量的作用域在写代码的时候就确定过了。
 2、基本上大括号里面定义的变量就 只能在大括号里面访问,和- Java 作用域类似。- var topLevel = true; main() { var insideMain = true; myFunction() { var insideFunction = true; nestedFunction() { var insideNestedFunction = true; print(topLevel); //true print(insideMain); //true print(insideFunction); //true print(insideNestedFunction); //true } //undefined name 'insideNestedFunction' //print(insideNestedFunction); nestedFunction(); } //undefined name 'insideFunction' //print(insideFunction); myFunction(); }
- 可选参数 
 1.可选参数包括:可选命名参数 和 可选位置参数
 2.但是这两种参数不能同时当做可选参数。
 3.可选参数的方法,在调用的时候,是可选的,可传入可不传入
 4.可选参数只能放到方法参数的末尾,不能放到必需参数的前面- 可选命名参数:
 定义方法时,可选命名参数需要将可选的参数放到{}中
 调用方法时,方法中的参数可以通过这种形式paramName: value来指定命名参数
 - main() { //name:张三;isMan:null;age:null printArgs("张三"); //name:李四;isMan:false;age:null printArgs("李四", isMan: false); //name:王五;isMan:null;age:88 printArgs("王五", age: 88); //name:赵六;isMan:true;age:55 printArgs("赵六", isMan: true, age: 55); } void printArgs(String name, {bool isMan, int age}) { print("name:$name;isMan:$isMan;age:$age"); }- 可选位置参数:
 定义方法时,可选位置参数需要将可选的参数放到[]中
 调用方法时,方法中的参数根据位置来指定命名参数
 - main() { //name:张三;isMan:null;age:null printArg("张三"); //name:李四;isMan:true;age:null printArg("李四", true); //无法调用 //printArg("王五", 29); //name:赵六;isMan:true;age:29 printArg("赵六", true, 29); } void printArg(String name, [bool isMan, int age]) { print("name:$name;isMan:$isMan;age:$age"); }
- 可选命名参数:
- 默认参数值 
 1、在定义方法的时候,可以使用 = 来定义可选参数的默认值。
 2、默认值只能是编译时常量。
 3、如果没有提供默认值,则默认值为 null。- void main() { //name:张三;age:39;isMain:true printArgs("张三", 39); //name:Lili;age:33;isMain:false printArgs("Lili", 33, isMan: false); } void printArgs(String name, int age, { bool isMan=true }) { print("name:$name;age:$age;isMain:$isMan"); }
- 测试函数是否相等 
 下面是测试顶级方法、静态函数和实例函数 相等的示例:- foo() {} // 一个顶级方法 class A { static void bar() {} // 一个静态方法 void baz() {} // 一个实例方法 } main() { var x; // 比较 顶级方法. x = foo; print(foo == x); // 比较静态方法 x = A.bar; print(A.bar == x); // 比较实例方法 var v = new A(); // A的第一个实例#1 var w = new A(); // A的第二个实例#2 var y = w; x = w.baz; // 这些闭包引用同一个实例(#2),因此它们是相等的 print(y.baz == x); // 这些闭包引用不同的实例,因此它们不等 print(v.baz != w.baz); }
4、闭包
- 特性: 
 1、一个- 闭包是一个方法对象。
 2、闭包定义在其他方法的内部,一般通过- return将其作为返回值返回。
 3、不管闭包对象(方法返回的)在何处被调用,该对象都可以访问其(即闭包所在的方法)作用域内的变量,并持有其状态。
- 示例: - void test() { Function add = makeAdder(1); int result = add(2); //result = 3 print("result = $result"); } /** * 定义返回方法的函数 */ Function makeAdder(num outerNum) { return (num innerNum)=> outerNum + innerNum; }
- 一段有意思的代码: - void main() { var callbacks = []; for (var i = 0; i < 3; i++) { // 在列表 callbacks 中添加一个函数对象,这个函数会记住 for 循环中当前 i 的值。 callbacks.add(() => print('Save $i')); } //[Closure: () => void, Closure: () => void, Closure: () => void] print(callbacks); callbacks.forEach((c) => c()); // 分别输出 Save 0 1 2 }- 说明: - for循环中,向- callbacks中加入的是一个- 匿名函数(此处定义的匿名函数的作用是打印局部变量- i),此函数持有循环中的变量- i(一般的,- i作为局部变量,循环结束就被回收了),即闭包的特性(持有外部方法中变量的状态)。
- forEach函数接收的是一个函数对象(匿名函数:- (c) => c())作为参数,每次循环进行调用此函数,即为- callbacks数组中的函数对象。
 
六、面向对象
1、定义
- Dart 是一个面向对象编程语言,同时支持基于 - mixin的继承机制。
- 每个对象都是一个类的实例,所有的类都继承于 Object。 
- 基于 Mixin 的继承 意味着每个类(Object 除外) 都只有一个超类,一个类的代码可以在其他多个类继承中重复使用。即一个类可以继承自多个父类。 
- 使用关键字 - calss声明一个类
- 使用关键字 - new创建一个对象,- new可以省略。
- 对象的成员包括方法和数据 (函数 和 实例变量)。 
- 示例:``` class Person { } - void main() { - Person p = new Person(); //省略new关键字 Person pp = Person();- } 
2、构造函数
- 定义: 
 1、定义一个和类名字一样的方法就定义了一个构造函数。
 2、还可以带有其他可选的标识符,形如- ClassName.identifier。- class Person { Person() { print("person===super"); } } class Student extends Person { //这里是继承父类,后面会总结 Student() { print("student====this"); } } void main() { Student s = Student(); //打印结果: //person===super //student====this }
- 默认构造函数 
 1、如果未显式定义构造函数,会默认一个空的构造函数。
 2、默认构造函数没有参数,并且会调用超类的没有参数的构造函数。- class Person { Person() { print("person===super"); } } class Student extends Person { //这里是继承父类,后面会总结 } void main() { Student s = Student(); //打印结果: //person===super }
- 自定义构造函数 
 1、如果存在自定义构造函数,则默认构造函数无效,即只能存在一个构造函数(这也验证了函数不能重载的特性)。
 2、其中- this关键字指当前的实例。- class Person { String name; int age; Person(String name, int age) { this.name = name; this.age = age; } //报错:The default constructor is already defined. Person() { print("person===super"); } }
- 语法糖 
 1、由于把构造函数参数赋值给实例变量的场景太常见了, Dart 提供了一个语法糖来简化这个操作:- class Person { String name; int age; /* //常规写法 Person(String name, int age) { this.name = name; this.age = age; }*/ //语法糖写法 Person(this.name, this.age); }
- 命名构造函数 - 说明: 
 1、使用命名构造函数可以为一个类实现多个构造函数
 2、使用命名构造函数来更清晰的表明你的意图
 3、构造函数不能继承,所以超类的命名构造函数也不会被继承。
- 实现方式: - 类名.方法。其中方法名称可以自定义
- 示例:``` class Person { - String name; int age; //语法糖写法 Person(this.name, this.age); //fromName名称可以随便起名 Person.fromName(String name) { this.name = name; }- //withAge名称可以随便起名 - Person.withAge(int age) { this.age = age; } void printArgs() { print("name:$name;age:$age"); }- } - void main() { - Person p = Person("张三", 18); //name:张三;age:18 p.printArgs(); Person pp = Person.fromName("李四"); //name:李四;age:null pp.printArgs(); Person ppp = Person.withAge(33); //name:null;age:33 ppp.printArgs();- } ``` 
 
- 常量构造函数 - 说明: 
 1、使用常量构造函数可以创建编译时常量,即类是不可变状态。
 2、使用- const声明构造方法,并且所有变量都为- final。 3、要使用常量构造函数只需要用 const 替代 new 即可,也可以省略- const。 4、两个一样的编译时常量其实是 同一个对象(通过- identical可进行对比)。
- 示例:``` class Person { - final String name; final int age; const Person(this.name, this.age); void printArgs() { print("name:$name;age:$age"); }- } - void main() { - const p = const Person("张三", 33); const pp = const Person("张三", 33); //true print(identical(p, pp)); //比较两个对象是否相等- } ``` 
 
- 工厂构造函数 - 说明: 
 1、如果一个构造函数并不总是返回一个新的对象,则可以将其定义为工厂构造函数。
 2、工厂构造函数,类似于设计模式中的工厂模式。
 3、在构造方法前添加关键字- factory实现一个工厂构造方法。
 4、在工厂构造方法中可以返回对象。
- 注意:工厂构造函数无法访问 - this。
- 示例: 
 下面代码演示工厂构造函数 如何从缓存中返回对象。``` class Logger { final String name; bool mute = false;- // _cache 是个库私有变量,在变量名前加 - _即为私有成员变量 static final Map<String, Logger> _cache =- <String, Logger>{};- //添加 factory 定义为工厂构造函数 factory Logger(String name) { - //如果缓存中有name,则取出返回,若不存在则添加并返回。 if (_cache.containsKey(name)) { return _cache[name]; } else { final logger = new Logger._internal(name); _cache[name] = logger; return logger; }- } - Logger._internal(this.name); - void log(String msg) { - if (!mute) { print(msg); }- } } - 调用:var logger = new Logger('UI'); logger.log('Button clicked'); ```
 
- 重定向构造函数 - 说明: 
 1、有时候一个构造函数会调动类中的其他构造函数,则可以通过重定向构造函数。
 2、一个重定向构造函数是没有代码的,在构造函数声明后,使用- :调用其他构造函数。
- 示例:``` class Person { - String name; int age; //语法糖写法 Person(this.name, this.age); Person.formAge(int age): this("张三", age); //此种写法没有给任何变量赋值,在调用后,name和age都为null Person.initParams(String name, int age); void printArgs() { print("name:$name;age:$age"); }- } ``` 
 
- 初始化列表 - 说明: 
 1、在构造函数体执行之前除了可以调用超类构造函数之外,还可以初始化实例参数。即初始化列表会在构造方法体执行前执行。
 2、使用- :设置初始化表达式,使用- ,分隔初始化表达式。
 3、初始化列表常用于设置- final变量的值。
 官网警告: 初始化表达式等号右边的部分不能访问- this。(本人验证,似乎并非如此。)
- 示例: 
 官网示例:``` class Point { num x; num y;- Point(this.x, this.y); - // Initializer list sets instance variables before // the constructor body runs. Point.fromJson(Map jsonMap) - : x = jsonMap['x'], y = jsonMap['y'] { print('In Point.fromJson(): ($x, $y)');- } } - 本地测试:/**- person 
- / class Person { String name; int age; bool isMan; - //初始化列表,加上了this Person(name, age): - this.name = name, this.age = age;- //初始化列表,加上了this Person.withMap(Map map): this.isMan = map["isMan"] { - this.name = map["name"]; this.age = map["age"];- } - void printArgs() { - print("name:$name;age:$age");- } 
 - } - /** - main 
- / import 'person.dart'; void main() { Person p = new Person("张三", 33); - //name:张三;age:33;isMan:null p.printArgs(); - Person pp = Person.withMap({ - "name": "李四", "age": 23, "isMan": true});- //name:李四;age:23;isMan:true pp.printArgs(); } ``` - 注:本人在测试的时候,在初始化列表上的变量加上了- this关键字,编译并未报错,也可以正常执行输出结果。
 这个地方不知是否是我的姿势有误,还是说确实可以如此使用,希望有知道的盆友可以解答我的困惑,非常感谢。- 不过,一切以官方为准,最好不要加上this
 
- 设置 - final变量:``` import 'dart:math';- class Point { final num x; final num y; final num distanceFromOrigin; - Point(x, y) - : x = x, y = y, distanceFromOrigin = sqrt(x * x + y * y);- } - main() { var p = new Point(2, 3); print(p.distanceFromOrigin); } 
 
3、成员(变量与函数)
- 说明: 
 1、对象的成员包括方法和数据 (函数 和 实例变量)。
 2、当你调用一个函数的时候,你是在一个对象上 调用:函数需要访问对象的方法 和数据。
 3、所有没有初始化的变量值都是 null。
 4、函数不能被重载,可以被子类覆写。
- 调用: - 使用点.来引用对象的变量或者方法。
- 使用 ?.来替代.可以避免当左边对象为null时候 抛出异常:
 - class Person { //定义实例变量 String name; int age; //定义实例函数 void printArgs() { print("name:${name};age:${age}"); } //演示方法不能被重载 //编译错误:The name 'printArgs' is already defined. void printArgs(int age) { print("name:${name};age:${age}"); } } void main() { Person p = new Person(); p.name = "张三"; p.age = 33; //name:张三;age:33 p.printArgs(); Person pp; pp?.name = "李四"; //报错:Unhandled exception: //NoSuchMethodError: The setter 'age=' was called on null. //Receiver: null //Tried calling: age=23 pp.age = 23; }
- 使用点
- 实例变量 - 说明: 
 1、所有没有初始化的变量值都是 null。
 2、每个实例变量都会自动生成一个- getter方法(隐含的)。
 3、非- final变量会自动生成一个- setter方法(隐含的)。
 4、如果你在实例变量定义的时候初始化该变量(不是 在构造函数或者其他方法中初始化),该值是在实例创建的时候 初始化的,也就是在构造函数和初始化参数列 表执行之前。
- 示例:``` class Point { num x; num y; } - main() { var point = new Point(); point.x = 4; // 调用x,是用了setter方法 assert(point.x == 4); // 调用x,是用了getter方法 assert(point.y == null); // 变量默认是null } ``` 
 
- 实例函数 - 说明 
 1、函数是类中定义的方法,是类对象的行为。
 2、对象的实例函数可以访问- this。
- 示例``` import 'dart:math'; - class Point { num x; num y; Point(this.x, this.y); - num distanceTo(Point other) { - var dx = x - other.x; var dy = y - other.y; return sqrt(dx * dx + dy * dy);- } } ``` 
 
- 计算属性-Getters And Setters - 说明 
 1、Getters 和 setters 是用来设置和访问对象属性的特殊函数。 2、每个实例变量都隐含的具有一个- getter, 如果变量不是- final的则还有一个- setter。 3、你可以通过实行- getter和- setter来创建新的属性, 使用- get和- set关键字定义- getter和- setter。 4、计算属性的值是通过计算而来,本身不存储值。
 5、计算属性赋值,其实是通过计算转换到其他实例变量。
 6、在开始使用实例变量,后来可以把实例变量用函数包裹起来,而调用你代码的地方不需要修改。
- 官网注意(本人此处还未搞明白什么意思) 
 1、像 (- ++) 这种操作符不管是否定义- getter都会正确的执行。 为了避免其他副作用, 操作符只调用- getter一次,然后把其值保存到一个临时变量中。
- 示例 - class Rectangle { num left; num top; num width; num height; Rectangle(this.left, this.top, this.width, this.height); // 定义两个计算属性:right 和 bottom. num get right => left + width; set right(num value) => left = value - width; num get bottom => top + height; set bottom(num value) => top = value - height; } main() { var rect = new Rectangle(3, 4, 20, 15); assert(rect.left == 3); rect.right = 12; assert(rect.left == -8); }
 
- 类变量和类函数(静态成员) - 说明 
 1、使用- static关键字来实现类级别的变量和函数
 2、静态成员不能访问非静态成员(this调用),非静态成员可以访问静态成员。- [静态函数不再类实例上执行, 所以无法访问 this- ]。 3、类中的常量需要使用- static const声明。- 4、静态变量对于类级别的状态是非常有用的。 
 5、静态变量在第一次使用的时候才被初始化。
 6、静态函数还可以当做编译时常量使用。例如,你可以把静态函数当做常量构造函数的参数来使用。
 注意:对于通用的或者经常使用的静态函数,考虑使用顶级方法而不是静态函数。
- 示例 - class Page { int x; static int currentPage = 1; static void upPage() { currentPage++; print("up--> currentPage = $currentPage"); } static void downPage() { //报错:Invalid reference to 'this' expression. //print(this.x); //不能访问非静态成员 currentPage--; print("down--> currentPage = $currentPage"); } } void main() { //1 print(Page.currentPage); //up--> currentPage = 2 Page.upPage(); //down--> currentPage = 1 Page.downPage(); }
 
- 抽象函数 
 详见抽象类
- 对象call方法(可调用的类) - 说明: 
 1、如果 Dart 类实现了- call()函数,则对象可以当做方法来调用。
 2、只要方法名为- call,无论有无参数、有无返回值,都是可以的
- 示例:``` class Person { - String name; int age; void call() { print("name:$name;age:$age"); }- } class Student { - String name; int age; void call(String name, int age) { print("name:$name;age:$age"); }- } class Worker { - String name; int age; String call(String name, int age) { return "name:$name;age:$age"; }- } - void main() { - Person person = Person(); //name:null;age:null person(); //因为实现了call方法,直接调用即可 Student student = Student(); //name:学生;age:15 student("学生", 15); Worker worker = new Worker(); String info = worker("工人", 33); //work==>name:工人;age:33 print("work==>$info");- } 
 
更多相关信息: Emulating Functions in Dart(在 Dart 中模拟方法)
4、继承与多态
- 定义 
 1、使用关键字- extends继承一个类
 2、子类会继承父类可见的属性和方法(可以用- @override注解来表明为覆写方法),不会继承构造方法
 3、子类能够覆写父类的方法、- getter和- setter
 4、Dart具有单继承、多态性
 5、Dart的多态性可以让子类实例指向父类的变量。- 示例``` class Person { - String name; int age; void work() { print("person--->working..."); } bool get isAdult => age > 18; void printArgs() { print("person==>name:$name;age:$age;isAdult:$isAdult"); }- } - class Student extends Person { - Student() { } Student.initParams(String name, int age) { this.name = name; this.age = age; } void study() { print("student--->studying..."); }
 
        @override
        bool get isAdult => age > 15;
        @override
        void printArgs() {
            //super.printArgs();
            print("student==>name:$name;age:$age;isAdult:$isAdult");
      }
    }
    void main() {
        Student student = Student();
        student.name = "张三";
        student.age = 16;
        //person--->working...
        student.work();
        //student==>name:张三;age:16;isAdult:true
        student.printArgs();
        //多态调用
        Person p = Student.initParams("李四", 17);
        //student==>name:李四;age:17;isAdult:true
        p.printArgs();
        if (p is Student) {
           //student--->studying...
           p.study();
        }
    } 
    ```- 继承中的构造函数 
 1、子类的构造方法默认会调用父类的无名无参构造函数。
 2、如果父类没有无名无参的构造函数,则需要显式调用父类构造函数。
 3、在构造方法参数后使用- :显式调用父类构造函数。
 4、如果有初始化列表,初始化列表要放在父类构造函数之前。- 注:子类不能使用 - 重定向构造函数(无方法体的- :设置变量的构造函数)进行自定义函数。_本人认为_:因为初始化参数要提前于父类构造函数,这个时候还不能使用- this(待验证)。- 示例
 - class Person { String name; int age; /* #1 Person() { print("person==="); } */ Person(this.name); Person.initParams(this.name, this.age); } class Student extends Person { //第一种 Student(String name) : super(name); //第二种 Student.initParams(String name, int age) : super.initParams(name, age); // 无法创建 // Student.initParams(String name, int age):this.name = name; } void main() { //#1 //打印:person=== //Student student = Student(); }- 初始化列表: - class Person { String name; int age; Person(this.name); Person.initParams(this.name, this.age); } class Student extends Person { final bool isMan; Student.withParams(String name, int age, bool man) : isMan = man, super.initParams(name, age); }- ``` 
- 构造方法执行顺序 
 1、父类的构造函数在子类构造函数的方法体开始执行的位置调用。
 2、如果有初始化列表,初始化列表会在父类构造函数之前执行。- 示例:``` class Person { - String name; int age; Person(this.name); Person.initParams(this.name, this.age) { print("Person.initParams===="); }- } - class Student extends Person { - final bool isMan; Student.withParams(String name, int age, bool man) : isMan = getMan(man), super.initParams(name, age) { print("Student.withParams===="); } static bool getMan(man) { print("Student===man:$man"); return man; }- } - void main() { - Student student = Student.withParams("学生", 17, true); /** * 打印如下: Student===man:true Person.initParams==== Student.withParams==== */- } ``` 
 
- 扩展: 
 查看所有超类- Object,其中有一些成员的前面用关键字- external来修饰,它的作用是根据不同平台(Dart是跨平台的语言)语言而有不同的具体实现。- /** * Returns a string representation of this object. */ external String toString();
5、抽象类
- 定义 
 1、抽象类是一个不能被实例化的类。
 2、使用- abstract修饰符定义一个 _抽象类_。
- 说明: 
 1、抽象类通常用来定义接口, 以及部分实现。
 2、如果你希望你的抽象类是可实例化的,则定义一个 工厂构造函数。 3、抽象类通常具有 抽象函数。 4、抽象类不能直接被实例化。
 5、抽象类可以没有抽象方法。
 6、有抽象方法的类一定要声明为抽象类。- 示例 - abstract class Person { void run(); void printArgs() { print("person==="); } } class Student extends Person { @override void run() { print("student run..."); } } void main() { Person p = new Student(); //student run... p.run(); }
- 注意: 
 官网说明:下面的类(示例)不是抽象的,但是定义了一个抽象函数,这样的类是可以被实例化的。
 但经过测试,确实是有警告,运行也会报错,不太明白这里所说的- 可以被实例化是什么意思。
 
```
 // This class is declared abstract and thus
   // can't be instantiated.
   abstract class AbstractContainer {
     // ...Define constructors, fields, methods...
     void updateChildren(); // Abstract method.
   }
   class SpecializedContainer extends AbstractContainer {
     // ...Define more constructors, fields, methods...
     void updateChildren() {
       // ...Implement updateChildren()...
     }
     // Abstract method causes a warning but
     // doesn't prevent instantiation.
     void doSomething();
   } 
```- 抽象函数 - 抽象方法不用abstract修饰,没有方法体实现。
- 实例函数、 getter、和 setter 函数可以为抽象函数。
- 抽象函数是只定义函数接口但是没有实现的函数,由子类来实现该函数。如果用分号来替代函数体则这个函数就是抽象函数。
- 调用一个没实现的抽象函数会导致运行时异常。
 
- 抽象方法不用
6、接口
- 定义 
 一个类通过使用关键字- implements来实现一个或者多个接口。
- 说明 
 1、类和接口是统一的,类就是接口。 2、一个类实现了某个接口,就要实现此接口的每个成员。
 3、如果是复用已有类的接口,使用继承(extends)。
 4、如果只是使用已有类的外在行为,使用接口(implements)。
 5、每个类都隐式的定义了一个包含所有实例成员的接口
- 示例:``` class Person { - String name; int get age => 18; void run() { print("run..."); }- } - class Student implements Person { - @override String name; @override // TODO: implement age int get age => 15; @override void run() { print("student run..."); }- } - void main() { - Student student = Student(); student.run();- } ``` 
- 建议: 
 1、将抽象类作为接口使用,让子类来实现(因为类即接口,所以可以通过关键字- implements来实现)。
 示例:``` abstract class Person {- void run();- } - class Student implements Person { - String name; int get age => 15; @override void run() { print("student run..."); }- } - void main() { - Student student = Student(); //student run... student.run();- } 
7、枚举类
- 定义 
 使用关键字- enum来定义一个枚举类型。
- 说明 
 1、枚举类型通常称之为- enumerations或者- enums,是一种特殊的类,用来表现一个固定数目的常量。
 2、枚举是一种有穷序列集的数据类型。
 3、常用于代替常量,控制语句等。
- 特性 
 1、枚举中的- index属性从0开始,依次累加。
 2、枚举中的- values属性可以列举出所有的枚举值。x
 3、无法显示的初始化一个枚举类型,即不能指定原始值(给枚举常量赋值)。
 4、无法继承枚举类型、无法使用 mixin、无法实现一个枚举类型。
- 示例: - enum Color { red, green, blue, //报错,不能指定原始值 //white = "#FFFFFF", } void main() { var color = Color.red; switch(color) { case Color.red: print("红色"); break; case Color.blue: print("蓝色"); break; case Color.green: print("绿色"); break; } //红色 //index==> 0 print("index==> ${color.index}"); List<Color> values = Color.values; //[Color.red, Color.green, Color.blue] print(values); }
8、Mixins
- 定义 
 使用 with 关键字后面为一个或者多个 mixin 名字来使用 mixin。
- 说明 
 1、Mixins 类似于多继承,实在多类继承中重用一个类代码的方式。
 2、作为Mixin的类不能显示声明构造函数,不能调用 super 。(由于沿着继承链传递构造函数参数的需要,该约束能避免出现新的连锁问题。)
 3、作为Mixin的类只能继承自- Object。
- 示例 - 继承示例: - class A { void a() { print("A.a..."); } } class B { void a() { print("B.a..."); } void b() { print("B.b..."); } } class C { void a() { print("C.a..."); } void b() { print("C.b..."); } void c() { print("C.c..."); } } class D extends A with B, C { } void main() { D d = D(); //C.a... d.a(); }- 说明: 
 这里打印了- C.a...是和继承的顺序有关的,如果将- D类继承- B和- C的顺序互换,则会调用- B中的方法,打印结果为- B.a...。
- 组合示例: - abstract class Engine { void work(); } class OilEngine implements Engine { @override void work() { print("Work with oil..."); } } class ElectricEngine implements Engine { @override void work() { print("Work with electric..."); } } class Tyre { String name; void run() {} } class Car = Tyre with ElectricEngine; class Bus = Tyre with OilEngine;- 说明: 
 这种方式一般可以实现模块的组装,将自己需要的模块进行组合,实现不同的功能。
 
- 官网说明: 
 从 Dart 1.13 开始, 这两个限制在 Dart VM 上 没有那么严格了:- Mixins 可以继承其他类,不再限制为继承 Object。
- Mixins 可以调用 super()。
 
- Mixins 可以继承其他类,不再限制为继承 
9、操作符
- 对象操作符 - ?.:条件成员访问- as:类型转换- is:判断是指定类型- is!:判断非指定类型- ..:级联操作,即可连续调用对象成员,因为会返回当前对象。
- 示例``` class Person { - String name; int age; void work() { print("person--->name:$name;age:$age"); }- } - void main() { - var p; p = ""; p = new Person(); //不会报错 p?.age = 43; p.name = "张三"; p.age = 43; //person--->name:张三;age:43 (p as Person).work(); if (p is Person) { //person--->name:张三;age:43 p.work(); } Person person = Person(); person..name = "Tom" ..age = 33 ..work(); //person--->name:Tom;age:33- } 
 
- 操作符覆写 - 说明: 
 1、操作符的覆写需要在类中定义。
 2、如果覆写了- ==,则还应该覆写对象的- hashCode和- getter函数。 关于 覆写- ==和- hashCode的示例请参考 实现 map 的 keys。
- 可覆写操作符 - < - + - > - / - ^ - []= - <= - ~/ - & - ~ - >= - * - << - == - – - % - >> 
- 格式:``` class Xxx { - 返回类型 operator 操作符(参数1, 参数2, 参数....) { 方法体 return 返回值; }- } ``` 
- 示例:``` class Person { - int age; Person(this.age); bool operator >(Person p) { return this.age > p.age; } int operator [](String ageParam) { if("age"==ageParam) { return this.age; } else { return 0; } } /** * 以下两个覆写方法,可以通过右键选择'Generate...' * 然后点击选择'== and hashCode'直接生成 */ @override bool operator ==(Object other) => identical(this, other) || other is Person && runtimeType == other.runtimeType && age == other.age; @override int get hashCode => age.hashCode;- } - void main() { - Person p1 = Person(22); Person p2 = Person(33); //p1>p2? ==> false print("p1>p2? ==> ${p1>p2}"); //p1.age==> 22 print("p1.age==> ${p1["age"]}");- } 
 
七、泛型
1、定义
- 方式 
 1、使用- <…>来声明泛型
 2、通常情况下,使用一个字母来代表类型参数, 例如- E,- T,- S,- K, 和- V等。
 3、- List是一个 泛型 (或者 参数化) 类型,定义为- List<E>。
- 使用泛型的原因 
 1、在 Dart 中类型是可选的,可以通过泛型来限定类型。
 2、使用泛型可以有效地减少重复的代码。 泛型可以在多种类型之间定义同一个实现,同时还可以继续使用检查模式和静态分析工具提供的代码分析功能。
 3、如果你需要更加安全的类型检查,则可以使用 参数化定义。
2、用法
- 类的泛型 - 说明: 
 在调用构造函数的时候, 在类名字后面使用尖括号(- <...>)来指定 泛型类型。
- 示例:``` class Cache - { - T value; T get() { return value; } void put(T value) { this.value = value; }- } - void main() { - Cache<String> cacheStr = Cache(); cacheStr.put("张三"); //张三 print(cacheStr.get()); Cache<int> cacheNum = Cache(); cacheNum.put(333); //333 print(cacheNum.get());- } ``` 
 
- 函数的泛型 - 说明:
 在函数上使用泛型,可以在如下地方使用类型参数(具体见示例):
 1、函数的返回值类型 (T)。
 2、参数的类型 (T value).
 3、局部变量的类型 (T temp).
 - 注意: 版本说明: 在 Dart SDK 1.21. 开始可以使用泛型函数。 - 示例:``` class Util { - static T put<T>(T value) { T temp; if (value!=null) { temp = value; } print("temp = $temp"); return value; }- } - void main() { - //张三 String value = Util.put<String>("张三"); //报错,提示类型错误 Util.put<String>(1);- } 
 
- 说明:
3、限制泛型类型
- 说明 
 当需要对泛型的具体类型进行限定的时候,可以使用- extends关键字来限定泛型参数的具体类型。
- 示例:``` // T must be SomeBaseClass or one of its descendants. class Foo - {...} - class Extender extends SomeBaseClass {...} - void main() { - // It's OK to use SomeBaseClass or any of its subclasses inside <>. var someBaseClassFoo = new Foo<SomeBaseClass>(); var extenderFoo = new Foo<Extender>(); // It's also OK to use no <> at all. var foo = new Foo(); // Specifying any non-SomeBaseClass type results in a warning and, in // checked mode, a runtime error. // var objectFoo = new Foo<Object>();- } 
八、库和可见性
1、简介
- Dart中的可见性以library为单位
- 默认情况下,每一个Dart文件就是一个库
- 使用_表示库的私有性。
- 使用import关键字导入库
2、示例:
- person.dart - /** * peerson */ class Person { String name; int age; Person(name, age): this.name = name, this.age = age; void printArgs() { print("name:$name;age:$age"); } }
- main - import 'person.dart'; void main() { Person p = new Person("张三", 33); //name:张三;age:33 p.printArgs(); }

image.png
九、异常
1、简介
- 代码中可以出现异常和捕获异常。 
- 异常表示一些未知的错误情况。 
- 如果异常没有捕获,则异常会抛出,导致抛出异常的代码终止执行。 
- 和 Java 不同的是,所有的 Dart 异常是非检查异常。 
- 方法不一定声明了他们所抛出的异常, 并且你不要求捕获任何异常。 
详情请参考 Exceptions 部分。
2、类型
注意:Dart 代码可以抛出任何非null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。
3、Throw(抛出异常)
- 可以抛出任意对象:throw 'Out of llamas!';
- 可以使用箭头函数=>: 抛出异常是一个表达式,所以可以在 => 语句中使用,也可以在其他能使用表达式的地方抛出异常。``` distanceTo(Point other) => throw new UnimplementedError();
4、Catch(捕获异常)
- 异常捕获的关键字 - on:捕获异常,指定异常类型- catch:捕获异常,捕获异常对象。- rethrow:重新抛出异常
- 说明: 
 1、捕获异常可以避免异常继续传递(你重新抛出rethrow异常除外)。捕获异常给你一个处理该异常的机会。
 2、对于可以抛出多种类型异常的代码,你可以指定多个捕获语句。每个语句分别对应一个异常类型,如果捕获语句没有指定异常类型,则该可以捕获任何异常类型。
 3、函数- catch()可以带有一个或者两个参数,第一个参数为抛出的异常对象,第二个为堆栈信息 (一个 StackTrace 对象)。
- 示例: - var foo = ''; void misbehave() { try { foo = "You can't change a final variable's value."; //此处演示异常 String sub = foo.substring(100); } on Exception catch (e) { print('Exception details:\n $e'); } catch (e, s) { print('misbehave() partially handled ${e.runtimeType}.'); print('Stack trace:\n$s'); rethrow; // Allow callers to see the exception. } } void main() { try { misbehave(); } catch (e) { print('main() finished handling ${e.runtimeType}.'); } } /** * 打印结果: misbehave() partially handled RangeError. Stack trace: #0 _StringBase.substring (dart:core/runtime/libstring_patch.dart:384:7) #1 misbehave (file:///Users/yu/Work/Workplace/Flutter/Dart/lesson2/exception_test.dart:7:26) #2 main (file:///Users/yu/Work/Workplace/Flutter/Dart/lesson2/exception_test.dart:19:9) #3 _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:300:19) #4 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12) main() finished handling RangeError. */
5、Finally
- 要确保某些代码执行,不管有没有出现异常都需要执行,可以使用 一个 - finally语句来实现。
- 如果没有 - catch语句来捕获异常,则在执行完- finally语句后,异常被抛出了。
- 定义的 - finally语句在任何匹配的- catch语句之后执行。
- 示例: - try { String sub = "123".substring(10); } catch(e) { print('Exception details:\n$e'); } finally { print("result........"); }
十、元数据
1、简介:
- 使用元数据可以给你的代码添加其他额外信息。
- 元数据可以在 library、class、typedef、type parameter、constructor、factory、function、field、parameter、或者variable声明之前使用,也可以在import或者export指令之前使用。
- 使用反射可以在运行时获取元数据 信息。
2、定义:
- 元数据注解是以 @字符开头,后面是一个编译时常量(例如deprecated)。
- 调用一个常量构造函数。
形如:@deprecated,@override
3、类型:
- Dart内置元数据注解: - @deprecated- @override- @proxy- class Television { /// _Deprecated: Use [turnOn] instead._ @deprecated void activate() { turnOn(); } /// Turns the TV's power on. void turnOn() { print('on!'); } }
- 自定义元数据注解: - library todo; class todo { final String who; final String what; const todo(this.who, this.what); }- 使用 @todo 注解的示例: - import 'todo.dart'; @todo('seth', 'make this do something') void doSomething() { print('do something'); }

 
  
  
  
 
 
 
