[TOC]
# 简介
[dart.dev](https://dart.dev/)
[dart.cn](https://dart.cn/)
项目地址:https://github.com/dart-lang
目前市面上几乎没有 **Dart 2** 相关的书籍,如果你想学习Flutter ,那么Dart ,也就成为了必须要具备的基础知识,官网就是个不错的地方,资料也很详细~
[dart-cheat-sheet](https://github.com/Temidtech/dart-cheat-sheet)
[Flutter-Cheat-Sheet](https://github.com/Temidtech/Flutter-Cheat-Sheet)
## 在线编写和运行 Dart
https://dartpad.dartlang.org/
https://dartpad.cn/
# Dart Libraries 和 第三方包管理
首先我们需要知道,Dart 是依赖于库的,从而实现相应的功能;
| 库名 | 作用 |
| --- | --- |
| `dart:core` | 内置类型,集合和其他核心功能。**每个Dart程序默认会自动导入该库** |
| `dart:async` | 异步编程通常使用回调函数,但Dart提供了备选方案:[Future](https://api.dartlang.org/stable/dart-async/Future-class.html) 和 [Stream](https://api.dartlang.org/stable/dart-async/Stream-class.html) 对象。未来就像是对将来某个时候提供结果的承诺。 Stream是一种获取值序列的方法,例如事件。 Future,Stream等等都在 dart:async 库([API参考](https://api.dartlang.org/stable/dart-async/dart-async-library.html))中。 |
| `dart:math` | `dart:math` 库(API参考)提供常见功能, 如正弦和余弦,最大值和最小值,以及常量,如pi 和 e。 Math库中的大多数功能都是作为顶级函数实现的。 |
| `dart:convert` | `dart:convert` 库(API参考)具有JSON和UTF-8转换器,并支持创建其他转换器。 [JSON](https://www.json.org/) 是一种用于表示结构化对象和集合的简单文本格式。 [UTF-8](https://en.wikipedia.org/wiki/UTF-8) 是一种常见的可变宽度编码,可以表示Unicode字符集中的每个字符。 |
当然还有其他的针对特定平台的包,如:[dart:io](https://www.dartlang.org/dart-vm/io-library-tour) 、[dart:html](https://webdev.dartlang.org/guides/html-library-tour) 等,你可以在这里查看[所有包的api](https://api.dartlang.org/stable)。
Flutter 包的 api:https://docs.flutter.io/
第三方包搜索:https://pub.dartlang.org/
## 库和可见性
`import` 和 `library`指令可以帮助您创建模块化和可共享的代码库。库不仅提供api,而且包含隐私部分:以下划线(`_`)开头的标识符仅在库中可见。**每个Dart应用程序都是一个库**,即使它不使用`library`指令。
可以使用包来分发库。[pub](https://www.dartlang.org/tools/pub) 是SDK中包含的**包管理器**。
## 使用库
导入的唯一必需参数是指定库的URI。对于内置库,URI具有特殊的`dart:` 格式(scheme)。对于其他库,您可以使用文件系统路径或 `package:` 格式。 `package:` 格式指定由包管理器(如 pub 工具)提供的库。例如:
```dart
import 'package:meta/meta.dart';
```
## 指定库前缀
通过 `as` 来制定库前缀,
```
// lib1,和lib2 都有 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;
```
## 懒加载一个库
要延迟加载库,首先必须使用 `deferred as` 导入库。
您可以在一个库上多次调用`loadLibrary()`,而不会出现任何问题。该库只加载一次。
```dart
import 'package:greetings/hello.dart' deferred as hello;
Future greet() async {
// 通过await ,暂停等待库的加载。有关async和await的详细信息,请参阅[异步支持](https://www.dartlang.org/guides/language/language-tour#asynchrony-support)
await hello.loadLibrary();
hello.printGreeting();
}
```
## 创建自己的库程序包
你可以[创建自己的库](https://www.dartlang.org/guides/libraries/create-library-packages)。
如何组织库源代码。
如何时使用 `export` 指令。
何时使用 `part` 指令。
[其他常用库](https://www.dartlang.org/guides/libraries/useful-libraries)
# [基础语法](https://www.dartlang.org/guides/language/language-tour)
## [关键字](https://www.dartlang.org/guides/language/language-tour#keywords)
![](https://box.kancloud.cn/e92dfaafda7899d97c18b6db64be0a75_556x496.png)
带有字样1的是内置标志符号,带有2字样的是Dart2新增的用于支持异步的关键字,其他的都是保留字!
## 语法规则
* 通过 `var` 声明变量而不指定类型;
* Dart 是强类型语言,但是声明变量时也可以不指定类型,因为**Dart 可以自动推断**,
```
var number = 16;
```
在上面的例子中,变量number 就被自动推断为`int` 类型
* 如果对象不限于单个类型,请[按照设计准则](https://www.dartlang.org/guides/language/effective-dart/design#do-annotate-with-object-instead-of-dynamic-to-indicate-any-object-is-allowed)指定 `Object` 或者 `dynamic` 类型。
```
dynamic name = 'Bob';
```
* Dart中所有的变量包括数字、函数和`null` 都是对象,每一个 对象都是一个类的实例,他们都继承于 `Object`。
* Dart 支持泛型,如`List<int>`表示集合元素类型为整型、`List<dynamic>` 表示元素类型为任何类型
* Dart 除了支持我们常见的静态函数(类直接调用)和普通函数(对象调用)外,同时也支持顶级函数如`main()` 和嵌套函数(函数里面的函数,也叫本地函数)
* 与函数类似,Dart 也支持顶级变量,同时支持静态变量和实例变量,实例变量也被叫做字段或属性
* 和 java 不同,Dart 没有 `public`、`protected`、`private`权限修饰符,在 Dart 里面的以下划线`_`开头的标志符表示是私有的
* Dart 里面的标志符号由`_`、字母和 数字组成,只能以`_`或者字母开头
* 要注意区分表达式和语句的不同,如`var a = 3 + 2`;整体是一个赋值语句,`=` 右边部分即 `3 + 2` 是一个表达式
* Dart 工具会向你发出两种类型的提醒:警告和错误。警告代表你的代码可能有问题,但是不会阻止程序的运行;**错误分为编译错误和运行错误**,前者会阻止程序的运行,后者则会在程序运行时抛出异常!
## Final 和 Const
在 Dart 中,未初始化的变量拥有一个默认的初始化值:`null`。即便数字也是如此,因为在 Dart 中一切皆为对象,数字也不例外。
`const` 表示编译时常量,即在代码还没有运行时我们就知道它声明变量的值是什么;
`final` 不仅有 `const` 的编译时常量的特性,最重要的它是运行时常量,并且 `final` 是惰性初始化,即在运行时第一次使用前才初始化
```dart
const int a = 1;
final int a = 8;
const b = false;
const c = a;
const d = 5 * 3;
final x = new DateTime.now(); // 正确
const x = new DateTime.now(); // 错误
final y = sin(90); // 正确
const y = sin(90); // 错误
```
## 内置类型
### Numbers
1. `int`
整数值不大于64位,具体取决于平台。
2. `double`
64位(双精度)浮点数,由IEEE754标准指定。
**`int` 和 `double` 都是 `num` 的子类型**
示例:
```
int x = 1;
int hex = 0xDEADBEEF;
double y = 1.1;
double exponents = 1.42e5;
```
### Booleans
Dart是强布尔类型检查,只有当值是 `true` 是才为真,其他都是 `false`,声明时用 `bool`
### Strings
```
//拼接字符串
var s5 = 'dd''ff'
"ee";
//上面的方法等价于
var s6 = 'dd' + 'ff' + "ee";
// 多行文本使用方法,三单引号 或者 双引号
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";
// 通过 在前面添加 r 来表示原生字符串,原本输出,不会解释变量和转义字符。
var s = r'In a raw string, not even \n gets special treatment $height.';
```
### Lists
在 Dart 语言中,具有一系列相同类型的数据被称为 List 对象。
Dart List 对象类似JavaScript 语言的 array 对象。
```dart
var list = [1, 2, 3]; //分析器自动推断list为List<int>,所以里面不能加入其他类型的元素
print(list.length); //3
list[1] = 11;
print(list.toString()); //[1, 11, 3]
var constantList = const [1, 2, 3];
// constantList[1] = 1; //因为前面的赋值使用了const 所以这句会报错.
constantList= [5]; //这样可以
```
### Maps
map 是一个包含 `key` 和 `value` 的对象,`key` 不能重复
```
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};
var gifts2 = new Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases2 = new Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
//获取value
print(gifts2['first']);// partridge;
print(gifts2.length);// 3
```
### runes
Dart 中 使用 `runes` 来获取UTF-32字符集的字符。String的 `codeUnitAt` 和 `codeUnit` 属性可以获取UTF-16字符集的字符
```dart
var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input));
```
### symbols
symbol 字面量是编译时常量,在标识符前面加`#`。如果是动态确定,则使用`Symbol` 构造函数,通过`new`来实例化.你可能永远也用不到 `Symbol
`
```
print(#s == new Symbol('s'));//true
```
# 函数
```dart
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null; //箭头函数,`=>` 实际为 `{ return expr; }`
// 可选命名参数
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}
// 可选位置参数
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
say('Bob', 'Howdy') ;
```
## 顶级函数
以下是测试顶级函数,静态方法和实例方法的示例:
```dart
void foo() {} // 顶级函数 A top-level function
class A {
static void bar() {} // A static method
void baz() {} // An instance method
}
void main() {
var x;
// Comparing top-level functions.
x = foo;
assert(foo == x);
// Comparing static methods.
x = A.bar;
assert(A.bar == x);
// Comparing instance methods.
var v = A(); // Instance #1 of A
var w = A(); // Instance #2 of A
var y = w;
x = w.baz;
// These closures refer to the same instance (#2),
// so they're equal.
assert(y.baz == x);
// These closures refer to different instances,
// so they're unequal.
assert(v.baz != w.baz);
}
```
## 可选参数
* 名称可选参数,调用一个函数时,你可以指定参数名称,语法 `paramName: value`。 举例:
```dart
enableFlags(bold: true, hidden: false);
```
那如何定义这样的函数呢?使用 `{param1, param2, …}`来指定名称参数:
```dart
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden = false}) {...}
```
**Flutter** 中实例创建表达式很复杂,所以 widget 构造时使用名称可选参数。这使得 实例创建表达式 更易读。
你可以使用`@required`注解来标注名称参数,在任何 dart 代码中,这表示这是一个 必填的参数。 举例:
```dart
const Scrollbar({Key key, @required Widget child})
```
当一个`Scrollbar`被构建的时候,如果缺失 `child`字段,语法会报错。
`Required`定义在 [meta](https://pub.dev/packages/meta) 包中。可以直接导入 `package:meta/meta.dart`,也可以导入其他包来暴露 `meta`, 比方说 `package:flutter/material.dart`。
* 可选的位置参数,用`[]`它们标记为可选的位置参数:
```dart
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
say('Bob', 'Howdy'); // 不带可选参数调用,结果是: Bob says Howdy
say('Bob', 'Howdy', 'smoke signal'); //结果是:Bob says Howdy with a smoke signal
```
## 参数默认值
下面的示例定义了一个函数 `doStuff()`,它为 `list` 参数指定了默认列表,为 `gifts` 参数指定了默认映射。
```dart
void doStuff(
{List<int> list = const [1, 2, 3],
Map<String, String> gifts = const {
'first': 'paper',
'second': 'cotton',
'third': 'leather'
}}) {
print('list: $list');
print('gifts: $gifts');
}
```
## `main` 函数
每一个程序都要有一个主函数,它是 `app` 的入口,无返回值,有一个 `List<String>`类型的可选参数。
下面是一个web app的主函数:
```
void main() {
querySelector('#sample_text_id')//获取页面上的button对象
..text = 'Click me!'//设置文本
..onClick.listen(reverseText);//设置监听
}
```
注:`..`是一种级联操作符,它返回调用者本身,这样就可以连续调用同一个对象上的多个方法,实现链式操作!
下面是一个带参的主函数,需要命令行操作:
```
//运行程序:dart args.dart 1 test
//args.dart 是程序文件名 1 test 是参数
void main(List<String> arguments) {
print(arguments.length);//2
print(int.parse(arguments[0]));//1
print(arguments[1]);//test
}
```
## 函数对象
* 函数可以作为一个参数
```dart
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);// 1 2 3
```
* 函数可以赋值给一个变量
```dart
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
print(loudify('hello'));//!!! HELLO !!!
```
上面用到了匿名函数,下面的将会介绍。
## 匿名函数
大多数函数都有名字,如 `main()`,没有名字的函数被称为匿名函数,有时也叫做`闭包`或 `lambda
`
```
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
// 上面的函数可以改写为箭头函数
list.forEach(
(item) => print('${list.indexOf(item)}: $item'));
// 0: apples 1: bananas 2: oranges
```
## 作用域
```
bool topLevel = true;
void main() {
var insideMain = true;
void myFunction() {
var insideFunction = true;
void nestedFunction() { // 该函数可以访问外部所有变量
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
}
}
```
## 词法闭包
```dart
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
return (num i) => addBy + i;
// 返回一个函数, addBy会被记得
}
void main() {
// Create a function that adds 2.
var add2 = makeAdder(2);
// Create a function that adds 4.
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
```
## 函数返回值
函数没有明确返回值时,`return null` 会被隐式添加。
```
foo() {}
```
# 运算符
## `==`
如果 x 或 y 为 `null` ,如果两者都为`null` 返回`true`,如果只有一个为`null` 返回`false`。
判断两个对象完全相同,使用 [`identical()`](https://api.dartlang.org/stable/dart-core/identical.html)
## 类型检测运算符
`as`、`is`、`is!` 在运行时检测类型。
```
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
// 您可以使用 as 运算符缩短上面的代码,如果 emp 是 null 或不是Person,as 检测到错误会抛出异常
(emp as Person).firstName = 'Bob';
```
## `??`操作符
如果第一个表达式值不为 `null`,返回它的值,否则就返回第二个值;
```
//如果b为 null,则将值赋给 b;否则,b 保持不变
b ?? = value;
String playerName(String name) => name ?? 'Guest';
// 等价于下面:Slightly longer version uses ?: operator.
String playerName(String name) => name != null ? name : 'Guest';
```
## `..`级联运算符(仅为特殊语法)
* 级联运算符`..`允许您对同一个对像进行一系列的操作。除了函数调用之外,还可以访问同一对象上的字段。这通常会为您节省创建临时变量的步骤,并允许您编写更流畅的代码。
```dart
querySelector('#confirm') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
```
* 上述例子相对于:
```dart
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
```
* 级联符号也可以嵌套使用。 例如:
```dart
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
```
* 当返回值是void时不能构建级联。 例如,以下代码失败:
```dart
var sb = StringBuffer();
sb.write('foo') // 返回void
..write('bar'); // 这里会报错
```
**注意: 严格地说,级联的..符号不是操作符。它只是Dart语法的一部分。**
> https://www.jianshu.com/p/9e5f4c81cc7d
## 有条件的成员访问符
```
foo?.bar //当foo为null时,foo?.bar 为null
```
## 流程控制语句
```
if (isRaining()) {
you.bringRainCoat();
} else if (isSnowing()) {
//if else 语句
you.wearJacket();
} else {
car.putTopDown();
}
var callbacks = [];
for (var i = 0; i < 2; i++) {
//for 语句
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c()); //forEach 语句
var collection = [0, 1, 2];
for (var x in collection) {
//for in 语句
print(x); // 0 1 2
}
for (int i = 0; i < candidates.length; i++) {
var candidate = candidates[i];
if (candidate.yearsExperience < 5) {
continue;
//跳入下一次循环
}
candidate.interview();
}
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
// 继续在nowClosed标签上执行。
nowClosed:
case 'NOW_CLOSED':
//CLOSED 和 NOW_CLOSED都会执行该语句
executeNowClosed();
break;
}
```
## 断言
第一个参数,可以是任何返回bool值的表达式,第二个参数,是显示的信息。
成功继续执行,如果为false,则断言失败并抛出异常([AssertionError](https://api.dartlang.org/stable/dart-core/AssertionError-class.html))。
```
assert(urlString.startsWith('https'),
'URL ($urlString) should start with "https".');
```
## 异常
```
throw 'Out of llamas!'; //抛出任何形式的异常。
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
//第一个是抛出的异常,第二个是堆栈跟踪( StackTrace 对象 )。
print('Exception details:\n $e');
print('Stack trace:\n $s');
} finally {
// 无论是否抛出异常,确保某些代码总能够运行,请使用 finally子句
cleanLlamaStalls(); // Then clean up.
}
```
要部分处理异常,同时不停止程序的执行,请使用 `rethrow` 关键字。
```
void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // Allow callers to see the exception.
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
```
# Class
## 常量构造函数
```dart
// 常量上下文,const 为可选
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
var a = const ImmutablePoint(1, 1); // Creates a constant
var b = ImmutablePoint(1, 1); // 此时没有const 修饰,又不在常量上下文中
assert(!identical(a, b)); // NOT the same instance!
```
## 对象运行时类型
`runtimeType`,该属性返回一个[Type](https://api.dartlang.org/stable/dart-core/Type-class.html) 对象
```
print('The type of a is ${a.runtimeType}');
```
## 构造函数
**子类不会从其父类继承构造函数,但自身也会有默认构造函数**。默认构造函数没有参数,并在超类中调用无参数构造函数。
```
class Point {
num x, y;
//在构造函数体运行之前, 用于设置x和y的语法糖
Point(this.x,this.y);
}
```
## 调用父类命名构造函数
```
class Person {
String firstName;
final num x;
final num y;
final num distanceFromOrigin;
Person.fromJson(Map data) {
print('in Person');
}
// 初始化那三个 final 字段
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
// Delegates to the main constructor.
Point.alongXAxis(num x) : this(x, 0);
}
class Employee extends Person {
// Person没有默认构造函数;调用父类命名构造函数。
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
main() {
var emp = new Employee.fromJson({});
// Prints:
// in Person
// in Employee
}
// ============
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
// 常量构造函数
const ImmutablePoint(this.x, this.y);
}
// ============
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to
// the _ in front of its name.
static final Map<String, Logger> _cache =
<String, Logger>{};
//关键字 `factory` 自定义构造函数逻辑,但是无法访问 this
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
```
## getter 和 setter
```
class Rectangle {
num left, top, width, 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;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
```
## 抽象方法
`abstract`修饰符
```
abstract class Doer {
// 定义实例变量和方法......
//...
void doSomething(); // 定义了一个抽象方法,所以类必须为抽象类
}
class EffectiveDoer extends Doer {
void doSomething() {
// 这里继承并实现对应的抽象方法
}
}
```
## `implements`
一个类实现多个接口:
```
// 一个 person 类, 隐式接口包含 greet().
class Person {
// In the interface, but visible only in this library.
final _name;
// Not in the interface, since this is a constructor.
Person(this._name);
// In the interface.
String greet(String who) => 'Hello, $who. I am $_name.';
}
// Person接口的实现。
class Impostor implements Person {
get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
String greetBob(Person person) => person.greet('Bob');
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
```
## `extends`
可以在子类中通过 `super`,引用父类。
```
class Television {
void turnOn() {
_illuminateDisplay();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
}
// ···
}
```
## `@override`
对于所有 Dart 代码有两种可用注解:`@deprecated`和`@override`。
使用 `@override` 注解(annotation) 表明要覆盖的成员。
```
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
```
> [元数据](https://www.dartcn.com/guides/language/language-tour#%E5%85%83%E6%95%B0%E6%8D%AE)
## [covariant](https://www.dartlang.org/guides/language/sound-problems#the-covariant-keyword)
在类型安全的代码中,缩小方法参数或实例变量的类型,可以使用 `covariant` (协变) 关键字
```
class Animal {
void chase(Animal x) { ... }
//这里是父类型
}
class Mouse extends Animal { ... }
class Cat extends Animal {
void chase(covariant Mouse x) { ... }
//通过用子类型覆盖参数的类型来收紧类型
}
```
## 可重写运算符
```
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);
// Operator == and hashCode not shown. For details, see note below.
// ···
}
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()
```
class A {
// 除非你覆盖noSuchMethod,否则使用 不存在的成员导致 NoSuchMethodError。
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
```
## 枚举类型
枚举类型(通常称为enumerations 或者 enums)是一种特殊类,用于表示固定数量的常量值。
使用 `enum` 关键字声明枚举类型:
```
enum Color { red, green, blue } // Color.red.index == 0,index 从0 开始
List<Color> colors = Color.values;
// 通过values 获取所有枚举值
assert(colors[2] == Color.blue);
```
## mixins
Mixins 是一种在多个类层次结构中 复用类代码的方法。
```
// 要实现一个 mixin,创建一个扩展 Object 的类,没有构造函数,并且没有调用 super
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
// 通过 with 关键字 使用 mixins
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
```
> [为类添加功能:Mixin](https://www.dartcn.com/guides/language/language-tour#%E4%B8%BA%E7%B1%BB%E6%B7%BB%E5%8A%A0%E5%8A%9F%E8%83%BD-mixin)
## static
对于常见或广泛使用的实用程序和功能,请考虑使用 **顶级函数** 而不是 静态方法。
您可以使用静态方法作为编译时常量。例如,您**可以将静态方法作为参数传递给常量构造函数**。
```
import 'dart:math';
class Point {
static const initialCapacity = 16;
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(Queue.initialCapacity == 16);
assert(2.8 < distance && distance < 2.9);
print(distance);
}
```
# 泛型 Generics
如果查看基本数组类型的API文档 [`List`](https://api.dartlang.org/stable/dart-core/List-class.html),您会看到该类型实际上是List <E>。 <...> 符号 将 List 标记为 generic (或 parameterized 参数化)类型 - 具有形式类型参数的类型。按照惯例,类型变量具有单字母名称,例如 E,T,S,K 和 V。
```js
// 集合字面量
var names = <String>['Seth', 'Kathy', 'Lars'];
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines'
};
// 泛型构造函数
var views = Map<int, View>();
//检测运行时 具体类型信息
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true 检测具体类型信息
```
## 限制泛型
通过 `extends` 关键字 限制 泛型
```js
// 通过 extends 关键字 限制 泛型
class Foo<T extends SomeBaseClass> {
// Implementation goes here...
String toString() => "Instance of 'Foo<$T>'";
}
class Extender extends SomeBaseClass {...}
// 可以SomeBaseClass或其任何子类作为泛型参数:
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
// 也可以不指定泛型参数:
var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'
// 其他情况会导致错误
var foo = Foo<Object>();
```
### 泛型方法
最初Dart 只能限制 class;最新语法,通过泛型方法,对函数、方法使用类型参数。
1. 在函数的返回中返回类型(T)
2. 在参数的类型中使用(List<T>)
3. 在局部变量的类型中(T tmp)
```dart
T first<T>(List<T> ts) {
// 做一些初步工作或错误检查,然后......
T tmp = ts[0];
// 做一些额外的检查或处理......
return tmp;
}
```
# 异步支持
Dart库中包含许多返回 [Future](https://api.dartlang.org/stable/dart-async/Future-class.html) 或 [Stream](https://api.dartlang.org/stable/dart-async/Stream-class.html) 对象的函数。这些函数是异步的:它们在进行可能耗时的操作(例如I / O)后返回,而不等待该操作完成。
`async` 和 `await` ,让我们可以编写类似同步的代码。
## 处理 Futures
1. 使用 `async`和 `await`。`
await expression` 的 `expression` 通常会被包裹为一个 `Future` 类型。
2. 使用Future API,如[库浏览中所述](https://www.dartlang.org/guides/libraries/library-tour#future)。
```dart
// await 需要的 定义为 async 的异步方法中,返回一个 Future 对象
Future checkVersion() async {
try {
version = await lookUpVersion();
// Do something with version
} catch (e) {
// 可以使用 try catch 进行相应处理
}
}
// main 函数 定义为一个异步函数
Future main() async {
checkVersion();
print('In main: version is ${await lookUpVersion()}');
}
```
## 异步函数
`Future<void>` 没有有效的返回值时,可以这样定义。
```
Future<String> lookUpVersion() async => '1.0.0';
```
## 处理 Streams
`await for` **不能对 UI事件监听器 使用**,因为UI事件可能会持续不断的存在。
1. 使用`async`和异步for循环(`await for`)。
2. 使用Stream API,如[库浏览中所述](https://www.dartlang.org/guides/libraries/library-tour#stream)。
```dart
await for (varOrType identifier in expression) {
// Executes each time the stream emits a value.
}
```
`expression`的值必须具有 Stream 类型。执行过程如下:
1. 等待直到流发出一个值。
2. 执行for 循环的主体,将变量设置为该发出的值。
3. 重复1 和 2,直到关闭流。
要停止侦听流,**可以使用 `break` 或 `return`语句**,该语句会从for循环中断开 并从流中取消订阅。
要在 `main()` 函数中使用异步for循环,`main()` 的主体必须标记为 ·async·:
```dart
Future main() async {
// ...
await for (var request in requestServer) {
handleRequest(request);
}
// ...
}
```
## 生成器函数
当您需要延迟地生成一个值序列时,请考虑使用生成器函数。Dart内置支持两种生成器函数:
* **同步生成器**:返回 [Iterable](https://api.dartlang.org/stable/dart-core/Iterable-class.html) 对象
* **异步生成器**:返回 [Stream](https://api.dartlang.org/stable/dart-async/Stream-class.html) 对象
要实现同步生成器函数,将函数体标记为 `sync*`,并使用 `yield` 语句传递值:
```dart
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
```
要实现异步生成器函数,将函数体标记为 `async*`,并使用 `yield` 语句传递值:
```js
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
```
如果您的生成器是递归的,您可以使用 `yield*` 来改进它的性能:
```js
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
//递归调用
}
}
```
有关生成器的更多信息,请参阅文章 [Dart语言异步支持:Phase 2](https://www.dartlang.org/articles/language/beyond-async)。
## 可调用的类
实现 `call()`方法可以让你的Dart 类像函数一样被调用。
```js
// WannabeFunction 类定义了一个 `call()` 函数
class WannabeFunction {
call(String a, String b, String c) => '$a $b $c!';
}
main() {
var wf = new WannabeFunction();
var out = wf("Hi", "there,", "gang");
// 类像函数一样被调用。
print('$out');
}
///执行结果
Hi there, gang!
```
有关类的更多信息,请参见[Dart中的模拟函数](https://www.dartlang.org/articles/language/emulating-functions)。
# 隔离器
大多数计算机,甚至在移动平台上,都有多核cpu。为了利用所有这些核心,开发人员通常使用同时运行的共享内存线程。但是,共享状态并发容易出错并且可能增加代码的复杂度。
不同于线程,所有Dart代码都运行在隔离器内部,而不是线程。每个隔离都有它自己的内存堆,确保任何其他隔离器都不能访问隔离状态。
有关更多信息,请参见 [dart:isolate库文档](https://api.dartlang.org/stable/dart-isolate) 。
## 类型定义
在 Dart 中,函数是对象,就像字符串和数字是对象一样。`typedef`或 `function-type`为函数提供一个类型别名,你可以在声明字段和返回类型时使用这个名称。当函数类型被分配给变量时,`typedef` 保留类型信息。
```js
typedef Compare = int Function(Object a, Object b);
class SortedCollection {
Compare compare;
SortedCollection(this.compare);
}
// Initial, broken implementation.
int sort(Object a, Object b) => 0;
void main() {
SortedCollection coll = SortedCollection(sort);
assert(coll.compare is Function);
assert(coll.compare is Compare);
}
```
*注意:目前,`typedefs` 仅限于函数类型。我们期望这种情况会改变。
*
因为 `typedef` 仅仅是别名,所以它们提供了一种检查任何函数类型的方法。例如:
```
typedef Compare<T> = int Function(T a, T b);
int sort(int a, int b) => a - b;
void main() {
assert(sort is Compare<int>); // True!
}
```
# 元数据 (Metadata )
`@deprecated` 和 `@override` 只有这两个注解在所有Dart 代码中,都是可用的。
```
class Television {
/// _Deprecated:改用 [turnOn] ._
@deprecated
void activate() {
turnOn();
}
/// Turns the TV's power on.
void turnOn() {...}
}
```
## 自定义元数据注解
元数据可以出现在library、class、typedef、类型参数、构造函数、factory、函数、字段、参数或变量声明之前,也可以出现在 `import` 或 `export`指令之前。您可以使用反射在运行时 检索元数据。
``` js
// 通过 `library` 指令 表明 todo库
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');
}
```
# 注释
* 单行注释 以 `//` 开头
```
void main() {
// TODO: refactor into an AbstractLlamaGreetingFactory?
print('Welcome to my Llama farm!');
}
```
* 多行注释 以 `/*` 开头
```
void main() {
/*
* This is a lot of work. Consider raising chickens.
Llama larry = Llama();
larry.feed();
larry.exercise();
larry.clean();
*/
}
```
**文档注释**是以`///`或`/**`开头的多行或单行注释。在连续行上使用///与多行文档注释具有相同的效果。
Dart编译器会忽略所有文本,除非它被括在括号中。使用括号,您可以引用类、方法、字段、顶级变量、函数和参数。括号中的名称在文档化程序元素的词法范围内解析。
```dart
/// 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文档的链接。
解析Dart代码并生成HTML文档,可以使用 [SDK的文档生成工具](https://github.com/dart-lang/dartdoc#dartdoc) 。
有关生成文档的示例,请参阅 [Dart API文档](https://api.dartlang.org/stable) 。
有关如何构造注释的建议,请参阅 [Dart文档注释的指南](https://www.dartlang.org/guides/language/effective-dart/documentation) 。
# 结束
本页概述了Dart语言中常用的功能。正在实施更多功能,但我们希望它们不会破坏现有代码。有关更多信息,请参阅[Dart语言规范](https://www.dartlang.org/guides/language/spec)和 [有效Dart](https://www.dartlang.org/guides/language/effective-dart)。
# 示例
```dart
import 'dart:math';
abstract class Shape {
num get area;
}
class Circle implements Shape {
final num radius;
//Circle(this.radius);
num get area => pi * pow(radius, 2);
}
class Square implements Shape {
final num side;
Square(this.side);
num get area => pow(side, 2);
}
main() {
final circle = Circle(2);
final square = Square(2);
print(circle.area);
print(square.area);
}
//=============================
import 'dart:math';
class Rectangle {
int width;
int height;
Point origin;
Rectangle({this.origin = const Point(0, 0), this.width = 0, this.height = 0});
@override
String toString() =>
'Origin: (${origin.x}, ${origin.y}), width: $width, height: $height';
}
main() {
print(Rectangle(origin: const Point(10, 20), width: 100, height: 200));
print(Rectangle(origin: const Point(10, 10)));
print(Rectangle(width: 200));
print(Rectangle());
}
class Bicycle {
int cadence;
int _speed = 0; //设置 只读变量
int get speed => _speed; //int get speed => _speed
int gear;
Bicycle(this.cadence, this.gear); //构造函数
// change 这 speed variable
void applyBrake(int decrement) {
_speed -= decrement;
}
void speedUp(int increment) {
_speed += increment;
}
// 重写默认方法
@override
String toString() => 'Bicycle: $_speed mph';
//带名字类似ES6中的胖箭头函数写法
}
// 主入口方法
void main() {
var bike = Bicycle(2, 1); //可以缺少 new
bike.applyBrake(1);
//调用方法
print(bike);
}
```