一个简单的Dart程序:
- 注释,单行、多行
- 数据类型、字面量、输出方式
- 字符串插值
- main()函数:特定的顶级函数
- 定义变量var:通过这种方式定义变量不需要指定变量类型
// 定义一个函数
printInteger(int aNumber) {
// 打印
print('The number is $aNumber)');
}
// 应用从这里开始
main() {
var number = 42;
printInteger(number);
}
重要的概念:
- 一切皆对象,所有对象都有对应的一个
类
的实例;无论数字、函数和null
都是对象;所有对象都继承自Object
类;
- Dart是强类型语言,但可以推断类型;如果要明确说明不需要任何类型,需要使用特殊类型
dynamic
动态类型;
- Dart支持泛型,如
List<int>
整数列表、List<dynamic>
任何类型的对象列表;
- Dart对函数的支持:
- 支持顶级函数main()
- 绑定在类上——静态函数
- 绑定在对象上——实例函数
- 支持函数内创建函数(嵌套或 局部函数)
- Dart对变量的支持:
- 支持顶级变量
- 绑定在类上——静态变量
- 绑定在对象上——实例变量(字段/属性)
- Dart没有关键字
public/protected/private
,如果标识符以下划线_
开头,则它相对于库是私有的;
- Dart表达式(运行时有值),语句(运行时无值);
condition?expr1:expr2
值可能是二者之一,if-else
语句没有值;语句可以包含表达式,但是表达式不能直接包含语句;
- Dart工具提示两种类型问题:警告 和 错误(编译时错误会阻止代码执行 或 运行时错误会导致代码在执行过程中引发异常);
Dart关键字解析:
-
abstract
:定义 抽象类 — 抽象类不能实例化;抽象类通常用来定义接口,以及部分实现。 如果希望抽象类能够被实例化,那么可以通过定义一个 工厂构造函数 来实现;
// 这个类被定义为抽象类,
// 所以不能被实例化。
abstract class AbstractContainer {
// 定义构造行数,字段,方法...
void updateChildren(); // 抽象方法。
}
-
as
、as
、is
、is!
运算符用于在运行时处理类型检查;
- 例如,
obj is Object
总是 true。 但是只有 obj 实现了 T 的接口时, obj is T
才是 true。
- 使用 as 运算符将对象强制转换为特定类型;
if (emp is Person) {
(emp as Person).firstName = 'Bob';
}
Future checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}
try {
version = await lookUpVersion();
} catch (e) {
// React to inability to look up the version
}
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
-
break
和 continue
:
- 使用 break 停止程序循环,使用 continue 跳转到下一次迭代;
- 如果对象实现了 Iterable 接口 (例如,list 或者 set)。 那么示例完全可以用另一种方式来实现:
while (true) {
if (shutDownRequested()) break;
processIncomingRequests();
}
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());
-
case
、switch
、default
:
- 在Dart中switch语句使用
==
比较比较整数,字符串,或者编译时常量
- 比较的对象必须都是同一个类的实例(并且不可以是子类), 类必须没有对 == 重写。
- 枚举类型 可以用于 switch 语句
- 在 case 语句中,每个非空的 case 语句结尾需要跟一个 break 语句;除 break 以外,还有可以使用 continue, throw,者 return。
- Dart 支持空 case 语句, 允许程序以
fall-through
的形式执行。
- 在非空 case 中实现
fall-through
形式, 可以使用 continue 语句结合 lable 的方式实现
- case 语句可以拥有局部变量, 这些局部变量只能在这个语句的作用域中可见。
var command = 'OPEN';
switch (command) {
case 'CLOSED':
executeClosed();
// break; // 缺省break会报错
case 'PENDING':
// executePending(); //但支持空case语句
// break;
case 'APPROVED':
executeApproved();
continue open;
case 'DENIED':
executeDenied();
break;
open:
case 'OPEN':
executeOpen();
break;
default:
executeUnknown();
}
-
catch
、finally
、rethrow
:捕获异常可以避免异常继续传递(除非重新抛出( rethrow
)异常)。 可以通过捕获异常的机会来处理该异常
- 通过指定多个 catch 语句,可以处理可能抛出多种类型异常的代码。
- catch 语句未指定类型, 则该语句可以处理任何类型的抛出对象
-
catch()
函数可以指定1到2个参数, 第一个参数为抛出的异常对象, 第二个为堆栈信息 ( 一个 StackTrace
对象 )。
- 如果仅需要部分处理异常, 那么可以使用关键字 rethrow 将异常重新抛出。
- 不管是否抛出异常, finally 中的代码都会被执行。 如果 没有用catch 匹配异常, 异常会在 finally 执行完成后,再次被抛出;任何匹配的 catch 执行完成后,再执行 finally ;
try {
breedMoreLlamas();
} on OutOfLlamasException {
// 一个特殊的异常
buyMoreLlamas();
} on Exception catch (e) {
// 其他任何异常
print('Unknown exception: $e');
} catch (e, s) {
// 没有指定的类型,处理所有异常
print('Something really unknown: $e');
rethrow;
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
// 抛出异常
throw FormatException('Expected at least 1 section');
// 抛出任意对象
throw 'Out of llamas!';
// 因为抛出异常是一个表达式, 所以可以在 => 语句中使用,也可以在其他使用表达式的地方抛出异常:
void distanceTo(Point other) => throw UnimplementedError();
-
class
、this
:class 用于声明类;
- 所有实例变量都生成隐式 getter 方法。 非 final 的实例变量同样会生成隐式 setter 方法
- 构造函数中,使用
this
关键字引用当前实例;仅当存在命名冲突时,使用 this
关键字。 否则,按照 Dart 风格应该省略 this ;(通常模式下,会将构造函数传入的参数的值赋值给对应的实例变量)
class Point {
num x; // 声明示例变量 x,初始值为 null 。
num y; // 声明示例变量 y,初始值为 null 。
num z = 0; // 声明示例变量 z,初始值为 0 。
// 生成构造函数
Point(num x, num y) {
// 还有更好的方式来实现下面代码,敬请关注。
this.x = x;
this.y = y;
}
}
-
const
和 final
:
- 使用过程中从来不会被修改的变量, 可以使用 final 或 const, 而不是 var 或者其他类型
- Final 变量的值只能被设置一次;Const 变量在编译时就已经固定 (Const 变量 是隐式 Final 的类型.)
- 实例变量可以是 final 类型但不能是 const 类型。
- 如果 Const 变量是类级别的,需要标记为 static const
- Const 关键字不仅可以用于声明常量变量,还可以用来创建常量值(
const
关键字在声明常量构造函数时
还有应用,参考关键字new
的描述)
// 声明常量变量
const bar = 1000000;
// 创建常量值
var foo = const [];
-
deferred
:
- Deferred loading (也称之为 lazy loading) 可以让应用在需要的时候再加载库
- 常用场景:减少 APP 的启动时间。执行 A/B 测试,例如 尝试各种算法的 不同实现。加载很少使用的功能,例如可选的屏幕和对话框。
- 延迟加载库的常量在导入的时候是不可用的,在导入文件的时候也无法使用延迟库中的类型;
// 要延迟加载一个库,需要先使用 deferred as 来导入
import 'package:greetings/hello.dart' deferred as hello;
// 当需要使用的时候,使用库标识符调用 loadLibrary() 函数来加载库:
Future greet() async {
// 可以多次调用 loadLibrary() 函数。但是该库只是载入一次
await hello.loadLibrary();
hello.printGreeting();
}
while (!isDone()) {
doSomething();
}
do {
printLine();
} while (!atEndOfPage());
-
dynamic
:动态的数据类型
-
else
if
:和 JavaScript 不同, Dart 的判断条件必须是布尔值,不能是其他类型
if (isRaining()) {
you.bringRainCoat();
} else if (isSnowing()) {
you.wearJacket();
} else {
car.putTopDown();
}
-
enum
:枚举类型也称为 enumerations 或 enums , 是一种特殊的类,用于表示数量固定的常量值
- 枚举中的每个值都有一个 index getter 方法, 该方法返回值所在枚举类型定义中的位置(从 0 开始)
- 使用枚举的 values 常量, 获取所有枚举值列表( list )
- 可以在 switch 语句 中使用枚举, 如果不处理所有枚举值,会收到警告
- 枚举不能被子类化,混合或实现
- 枚举不能被显式实例化
enum Color {
red, green, blue }
assert(Color.red.index == 0);
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
-
export
:
- 库代码位于lib目录下,对其他包是公开的。您可以根据需要在lib下创建任何层次结构。按照惯例,实现代码放在lib/src下。lib/src下的代码被认为是私有的;其他包永远不需要导入src/…要使lib/src下的api公开,可以从直接位于lib下的文件导出lib/src文件;
// 目录结构
- src
- cascade.dart
- ...
- shelf.dart
- shelf_io.dart
// shelf.dart, exports several files from lib/src:
export 'src/cascade.dart';
export ...
-
extends
和 super
:使用 extends 关键字来创建子类, 使用 super 关键字来引用父类
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
-
factory
:工厂构造函数
- 当执行构造函数并不总是创建这个类的一个新实例时,则使用 factory 关键字。
- 一个工厂构造函数可能会返回一个 cache 中的实例, 或者可能返回一个子类的实例。
- 工厂构造函数无法访问 this。
class Logger {
final String name;
bool mute = false;
// 从命名的 _ 可以知,
// _cache 是私有属性。
static final Map<String, Logger> _cache =
<String, Logger>{
};
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);
}
}
// 工厂构造函的调用方式与其他构造函数一样
var logger = Logger('UI');
logger.log('Button clicked');
-
false
和 true
-
for
:
- 闭包在 Dart 的 for 循环中会捕获循环的 index 索引值, 来避免 JavaScript 中常见的陷阱
- 如果要迭代一个实现了 Iterable 接口的对象, 可以使用 forEach() 方法
- 实现了 Iterable 的类(比如, List 和 Set)同样也支持使用 for-in 进行迭代操作 iteration
var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
message.write('!');
}
var callbacks = [];
for (var i = 0; i < 2; i++) {
// 输出的是 0 和 1。 但是示例中的代码在 JavaScript 中会连续输出两个 2
callbacks.add(() => print(i));
}
// 如果不需要使用当前计数值, 使用 forEach() 是非常棒的选择;
callbacks.forEach((c) => c());
var collection = [0, 1, 2];
for (var x in collection) {
print(x); // 0 1 2
}
-
Function
:
- Dart 是一门真正面向对象的语言, 甚至其中的函数也是对象,并且有它的类型 Function;
- 这也意味着函数可以被赋值给变量或者作为参数传递给其他函数。
-
箭头 语法
:=> expr;
语法是 { return expr; }
的简写;
- 在箭头 (
=>
) 和分号 (;
) 之间只能使用一个 表达式 ,不能是 语句
- 函数有两种参数类型: required 和 optional。 required 类型参数在参数最前面, 随后是 optional 类型参数。 命名的可选参数也可以标记为 “
@required
”
- 可选参数可以是
命名参数
或者位置参数
,但一个参数只能选择其中一种方式修饰。
-
命名参数 & 位置参数:
- 默认值只能是编译时常量。 如果没有提供默认值,则默认值为 null。
- list 或 map 可以作为默认值传递;
// 位置参数声明方式
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
// 位置参数的调用方式
isNoble(100);
// 命名参数声明方式
void enableFlags({
bool bold, bool hidden= false}) {
...}
// 命名参数的调用方式
enableFlags(bold: true, hidden: false);
// Flutter 创建实例的表达式可能很复杂, 因此窗口小部件构造函数仅使用命名参数
const Scrollbar({
Key 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;
}
// list 或 map 可以作为默认值传递
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');
}
Required 被定义在 meta package。 无论是直接引入(import) package:meta/meta.dart
,或者引入了其他 package,而这个 package 输出(export)了 meta,比如 Flutter 的 package:flutter/material.dart
。
-
implements
:隐式接口
- 每个类都隐式的定义了一个接口,接口包含了该类所有的实例成员及其实现的接口。 如果要创建一个 A 类,A 要支持 B 类的 API ,但是不需要继承 B 的实现, 那么可以通过 A 实现 B 的接口。
- 一个类可以通过 implements 关键字来实现一个或者多个接口, 并实现每个接口要求的 API。
// 一个类对应一个 隐式的接口Person
class Person {
// 包含在接口里,但只在当前库中可见。
final _name;
// 不包含在接口里,因为这是一个构造函数。
Person(this._name);
// 包含在接口里。
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()));
}
// 实现多个接口
class Point implements Comparable, Location {
...}
-
get
和 set
:
- Getter 和 Setter 是用于对象属性读和写的特殊方法
- 使用 get 和 set 关键字实现 Getter 和 Setter ,能够为实例创建额外的属性。
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个计算属性: right 和 bottom。
num get ri