Dart 简介

Dart是一种适用于万维网的开放源代码编程语言,由Google主导开发,于2011年10月公开。它的开发团队由Google Chrome浏览器V8引擎团队的领导者拉尔斯·巴克主持,目标在于成为下一代结构化Web开发语言。

类似JavaScript,Dart也是一种面向对象语言,但是它采用基于类编程。它只允许单一继承,语法风格接近C语言。

发展历史

2011年Google在丹麦奥胡斯(Aarhus)举行的“GOTO国际软件开发大会”预告将发布新网页编程语言Dart,是一种基于类编程语言(class-based programming language),在所有浏览器都能够有高性能的运行效率。2011年10月10日Google宣布推出Dart的预览版。

Google在dartlang.org公布Dart开发工具及源代码示例等内容,同时也提供相关虚拟机平台。当前Dart有两种方式运行:一是在本地的虚拟机上,二是将Dart代码转成Javascript,直接在Javascript引擎上运行

稳定版本 2.1 , 在2018年11月15发布

标准化

  • ECMA已经成立技术委员会TC52[6]展开标准化Dart的工作,同时由于Dart能够被编译成标准的JavaScript,它能够有效地在所有现代浏览器上运行。2014年7月,ECMA的第107次全体大会通过了第一版Dart语言规范

Dart SDK 构成

  • SDK 包含 Dart VM、dart2js、Pub、库和工具

Dart 编译模式

  • Dart 支持 JIT (Just In Time) 运行时编译和 AOT (Ahead Of Time) 预编译

Dart 优势

  • Dart 可以更轻松地创建以 60fps 运行的流畅动画和转场。Dart 可以在没有锁的情况下进行对象分配和垃圾回收。就像 JavaScript 一样,Dart 避免了抢占式调度和共享内存(因而也不需要锁)。由于 Flutter 应用程序被编译为本地代码,因此它们不需要在领域之间建立缓慢的桥梁(例如,JavaScript 到本地代码)。它的启动速度也快得多
  • Dart 使 Flutter 不需要单独的声明式布局语言,如 JSX 或 XML,或单独的可视化界面构建器,因为 Dart 的声明式编程布局易于阅读和可视化。所有的布局使用一种语言,聚集在一处,Flutter 很容易提供高级工具,使布局更简单
  • Dart 跨平台性更强
  • Dart 入门更简单

Dart 资源汇总

Dart 官网: https://www.dartlang.org

Dart 在线代码运行: https://dartpad.dartlang.org

Dart API : https://api.dartlang.org/stable/2.1.0/index.html

Dart 项目地址: https://github.com/dart-lang

Dart2

  • Dart 2 专注于改善构建客户端应用程序的体验,包括加快开发人员速度、改进开发人员工具和类型安全。例如,Dart 2 具有坚实的类型系统和类型推理。
  • Dart 2 还使new和const关键字可选。这意味着可以在不使用任何关键字的情况下描述 Flutter 视图,从而减少混乱并且易于阅读。例如:
Widget build(BuildContext context) =>
  Container(
    height: 56.0,
    padding: EdgeInsets.symmetric(horizontal: 8.0),
    decoration: BoxDecoration(color: Colors.blue[500]),
    child: Row(
      ...
    ),
  );

Dart 重要概念

  • 可以放到变量的一切内容结尾对象, 所有的对象都是类的实体, 所有的对象都是继承自 Object 类
  • Dart 是强类型, 但可以不声明变量类型, 它能推断变量的类型
  • Dart 支持泛型 eg.List<dynamic>
  • Dart支持顶级函数(如main()),以及绑定到类或对象的函数(分别是静态方法和实例方法)。您还可以在函数中创建函数(嵌套的或本地的函数)
  • 类似地,Dart支持顶级变量,以及绑定到类或对象(静态变量和实例变量)的变量。实例变量有时称为字段或属性
  • 与 Java c++ 不同, Dart没有 public private protect 关键字, 变量以下划线(_)开头则表示是库私有的(详见https://www.dartlang.org/guides/language/language-tour#libraries-and-visibility)
  • 标识符以字母或者下划线开头
  • Dart既有表达式(有运行时值),也有语句(没有运行时值)。例如,条件表达式条件?expr1: expr2的值为expr1或expr2。将其与if-else语句进行比较,后者没有值。语句通常包含一个或多个表达式,但表达式不能直接包含语句。
  • Dart工具可以报告两类问题:警告和错误。警告只是表明您的代码可能无法工作,但它们不会阻止您的程序执行。错误可以是编译时的,也可以是运行时的。编译时错误会阻止代码执行;运行时错误会在代码执行时引发异常。

变量

  • 引用声明方式(推荐)
  var name = 'cnvoid.com';

变量存储一个字符串类型的引用

  • 动态声明变量
 dynamic name = 'cnvoid.com';
  • 显示声明变量, 指定变量类型
  String name = 'cnvoid.com';

未初始化的变量默认值均为 null

未初始化的变量需要初始化之后才能进行运算操作_

常量 final 和 const

区别

  • final 变量 只能设置一次, 在第一次使用时被初始化
  • const 变量是编译时常量, 是隐式 final. const 变量在类级别, 标记为 static const. 在编译时设置 const 值; const 不仅可以什么常量变量, 也可以声明常量值

final 声明

final name = 'cnvoid.com';//不声明类型
finam String name = 'cnvoid.com'; //带类型声明

const 声明

const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere
var foo = const [];
final bar = const [];
const baz = []; // Equivalent to `const []`

内置类型

  • numbers
  • strings
  • booleans
  • lists (also known as arrays)
  • maps
  • runes (for expressing Unicode characters in a string)
  • symbols

函数(和js比较类似)

函数也是对象, 属于Function 对象; 可以将函数分配给其他对象, 或者作为参数传递给其他函数

含有注释的函数实现(官方推荐)

bool isNoble (int atomicNumber ){
   return _nobleGases [ atomicNumber ] != null ;
}

省略注释的(强大的Dart引擎能自动识别)

isNoble (atomicNumber ){
  return _nobleGases [ atomicNumber ] != null ;
}

只有一句表达式的函数简写(使用=>箭头)

bool isNoble (int atomicNumber )=> _nobleGases [ atomicNumber ] != null ;

函数可以有两种类型的参数:必需和可选。首先列出所需参数,然后列出任何可选参数。命名的可选参数也可以标记为@required。

可选参数

可选参数可以是位置参数,也可以是命名参数,但不能同时存在!!!

可选的命名参数

  • 定义函数时, 使用 {param1, param2,…} 表示:
// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}
  • 调用函数时, 通过使用 param: value方式指定参数名:
enableFlags(bold: true, hidden: false);
  • 在任何 Dart 语言(不仅仅Flutter中)在参数前面添加 @required注解标识该参数必传:
const Scrollbar ({ Key key ,@required Widget child })

可选位置参数

这个就和javascript 的可选参数一样的.

  • 定义位置默认参数 [device]:
String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}
  • 调用函数不传可选参数:
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
  • 调用传可选参数
assert(say('Bob', 'Howdy', 'smoke signal') ==
    'Bob says Howdy with a smoke signal');

两种可选参数在定义时的不同: 可选命名参数用{}定义可选参数, 可选位置参数定义时使用[]定义可选参数

默认参数值(使用=给两种可选参数赋予默认值, 默认为null)

  • 命名可选参数默认值定义:
// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold = false, bool hidden = false}) {...}

// bold will be true; hidden will be false.
enableFlags(bold: true);
  • 位置可选参数默认值定义
String say(String from, String msg,
    [String device = 'carrier pigeon', String mood]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  if (mood != null) {
    result = '$result (in a $mood mood)';
  }
  return result;
}

assert(say('Bob', 'Howdy') ==
    'Bob says Howdy with a carrier pigeon');
  • 对列表类型和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');
}

main() 方法

所有的应用都必须具有顶级的main()入口, main() 返回void, 有List<String>参数可选

void main() {
  querySelector('#sample_text_id')
    ..text = 'Click me!'
    ..onClick.listen(reverseText);
}

函数, 作为一级对象

  • 可以将函数作为参数传给另一个函数:
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 !!!');

匿名函数

  • 匿名函数定义:
([[Type] param1[, …]]) {
  codeBlock;
};
  • 匿名函数使用:
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});
```(dart)
- 只有一条语句的简写:

```(dart)
list.forEach(
    (item) => print('${list.indexOf(item)}: $item'));

词法作用域

  • nestedFunction() 可以使用所有层级的变量
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);
    }
  }
}

词法闭包

闭包是能访问内部作用域变量的函数对象.

/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

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);
}

测试函数是否相等

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);
}

函数返回值

所有函数都返回一个值。如果未指定返回值,则将语句return null;隐式附加到函数体。

操作符

描述操作符
一元后缀操作符expr++ expr– () [] . ?.
一元前缀操作符-expr !expr ~expr(按位取反)) ++expr –expr
乘法运算符* / % ~/
加法运算符+ -
移位运算符« »
按位 与&
按位异或^
按位或|
关系和类型判断>= > <= < as is is!
等于运算符== !=
逻辑运算符&&|| !
条件运算符 if null??(例如: var a = null ?? 3)
三元条件运算符expr1 ? expr2 : expr3
级联运算符..
赋值运算符= *= /= ~/= %= += -= «= »= &= ^=
  • 运算符优先级从上表中从上到下, 优先级逐行递减

类型检查

  • as is is! 运行时检查类型
操作符解释
as类型转换(也用于库前缀)
is变量是特定类型时为 true
is!变量为特定类型时为 false
  • obj is T == true : 表示obj实现了T定义的接口, e.g obj is Object 始终为 true.
  • 判断类型, 对变量赋值:
if (emp is Person) {//如果emp不是Person, 不执行
  // Type check
  emp.firstName = 'Bob';
}

简化代码:

  (emp as Person).firstName = 'Bob'; //如果emp不是Person 报异常
  • as 只能在一条继承链上实现转换

赋值运算符

  • 普通赋值
var a = value

判断是否为空赋值

b ??=value //如果b为null, 将value赋予b, 如果b非null, b值不变
  • 复合赋值运算符
复合赋值等价表达式
运算符 opa op ba = a op b
例如:a~/= ba = a ~/ b
  • 逻辑运算符: && - 与; || - 或; ! - 非
  • 位运算符
OperatorMeaning
&AND
|OR
^XOR
~exprUnary bitwise complement (0s become 1s; 1s become 0s)
«Shift left
»Shift right
  • 条件表达式
    • condition ? expr1 : expr2
    • expr1 ?? expr2 如果expr1为null, 取 wxpr2, 否则取expr1

级联符号(..) - 链式调用, 函数式

  • 级联(..)允许你对同一对象进行一系列操作. 除了可以函数调用, 还可以访问同一对象上的字段.
querySelector('#confirm') // Get an object.
 ..text = 'Confirm' // Use its members.
 ..classes.add('important')
 ..onClick.listen((e) => window.alert('Confirmed!'));

其他操作符

() 函数调用 [] 列表取值 . 成员访问 ?. 条件成员访问(最左边操作可以为null)

流程控制语句(和其他语言一样, 不在累述)

  • if .. else
  • for
  • while do..while
  • break continue
  • switch case
  • assert
  • try-catch throw

异常处理

  • 抛出异常
throw FormatException('Expected at least 1 section');
throw 'Out of llamas!';
  • 捕获异常
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');
}
  • Finally
try {
  breedMoreLlamas();
} finally {
  // Always clean up, even if an exception is thrown.
  cleanLlamaStalls();
}

类 Class

定义一个类以及实例变量

class Point {
  num x; // Declare instance variable x, initially null.
  num y; // Declare y, initially null.
  num z = 0; // Declare z, initially 0.
}

构造函数 Constructors

以类名为函数名创建函数:

class Point {
  num x, y;

  Point(num x, num y) {
    // There's a better way to do this, stay tuned.
    this.x = x;
    this.y = y;
  }
}
  • this 指向当前实例, 只有当有名字冲突才使用this, 否则Dart风格默认缺省this

使用类成员, 使用操作符

var p = Point(2, 2);

// Set the value of the instance variable y.
p.y = 3;

// Get the value of y.
assert(p.y == 3);

// Invoke distanceTo() on p.
num distance = p.distanceTo(Point(4, 4));

使用 ?. 替换 . 去避免左边操作符为null时的异常:

// If p is non-null, set its y value to 4.
p?.y = 4;

使用构造函数

可以使用构造函数创建实例对象, 构造函数可以是ClassName也可以是ClassName.identifier, 构造函数不返回值

var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
  • Dart2后, new 关键字为可选
  • 常量构造函数(我的理解就是单例模式)
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // They are the same instance!
  • 同一个常量上下文中*, 可以缺省构造函数前面的const(可以省略除了第一个使用的const), Dart2特性:
// Lots of const keywords here.
const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
// Only one const, which establishes the constant context.
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
  • 获取对象类型 在运行时获取对象类型, 可以使用超类Object的runtimeType获取:
class Cnvoid {
  var firstName = '333';
  static void bar() {} // A static method
  void baz() {} // An instance method
}

void main() {
  var a = Cnvoid();
print('${a.runtimeType}'); //Cnvoid
}
  • 默认构造函数 在没有声明构造函数的情况下, 会默认提供一个没有参数的构造函数.
  • 构造函数不可继承
  • 命名构造函数

命名构造函数可以实现一个类存在多个构造函数

class Point {
  num x, y;

  Point(this.x, this.y);

  // Named constructor
  Point.origin() {
    x = 0;
    y = 0;
  }
}
  • 调用没有默认父类的构造方法

执行顺序如下:

  1. 构造器列表
  2. 父类梅参数构造器
  3. 当前类无参构造器执行

**如果父类没有未命名没有参数的构造函数, 必须手动调用父类的一个构造函数. 调用方式是在当前构造函数体前用(:)调用:

class Person {
  String firstName;
  Person () {
    print('in Person default constructor');
  }
  Person.fromJson(Map data) {
    print('in Person');
  }
}

class Employee extends Person {
  // Person does not have a default constructor;
  // you must call super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

main() {
  var emp = new Employee.fromJson({});

  // Prints:
  // in Person
  // in Employee
  if (emp is Person) {
    print('tttt');
    // Type check
    emp.firstName = 'Bob';
  }
  (emp as Person).firstName = 'Bob';
}

构造器列表

除了在调用父类构造函数外,还可以在构造函数提运行前初始化实例变量,用逗号分隔开构造器:

// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}

-右侧初始化没有访问this的权限

  • 在开发阶段, 开发者可以在初始化列表使用 assert 断言验证输入:
Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}

重定向构造器

有时后为了在同一个类重定向到另一个构造器而使用.重定向构造器没有函数体,在(:)之后调用

class Point {
  num x, y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(num x) : this(x, 0);
}

常量构造函数 - 单例

如果类对象永远不会改变, 则将变量设置为编译时常量. 定义一个const构造函数, 并确保所有的实例变量都是final!!

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}

工厂构造器

使用 factory 关键实现的构造函数不会一直创建新的实例. 一个工厂构造函数可能会返回一个缓存的实例, 或者返回子类的实例.工厂构造器没有this的访问权限

工厂构造函数返回缓存实例:

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 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');

方法

方法是给对象提供的功能函数.

实例方法

对象上的实例方法可以访问实例变量和this:

import 'dart:math';

class Point {
  num x, 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 和 Setters

Getters 和 Setters 是特殊的方法, 用于读取和写入对象属性. 每一个实例变量都有一个隐含的 getter setter 方法. 通过使用 get set关键字实现getter和setter创建一个附加属性.

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // Define two calculated properties: right and 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);
}

-在类似(++)的操作符工作时, 将按预期的方式运行, 无论getter或setter是否定义. 为了避免非预期的影响, 将他的值存在临时变量中

抽象方法

-抽象方法只能存在抽象类中 定义抽象方法, 用分号替代函数体.

abstract class Doer {
  // Define instance variables and methods...

  void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // Provide an implementation, so the method is not abstract here...
  }
}

抽象类

抽象类用于定义接口, 通常拥有一些实现. 使用abstract修饰符来定义抽象类.抽象类不可以实例化.

// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
  // Define constructors, fields, methods...

  void updateChildren(); // Abstract method.
}

隐式接口

每一个类都有一个隐式接口, 包含了类的所有的实例成员和它实现的接口. 如果想要创建class A 实现class B的实现, 但不继承class B, class A 需要实现 class B 的接口:

// A person. The implicit interface contains 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.';
}

// An implementation of the Person interface.
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 {...}

继承类

使用 extends 关键字创见类, super 指向父类:

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}

class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}

重写成员

子类可以重写实例方法,getters, setters. 使用 @override 注解声明重写成员:

class SmartTelevision extends Television {
  @override
  void turnOn() {...}
  // ···
}

重写操作符

-操作符 != 不可以重写, 需要重写相同功能使用!(e1 == e2)实现

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));
}

-** 若需要重写 ==, 需要同时重写 Object.hasCode 的getter**

class Person {
  final String firstName, lastName;

  Person(this.firstName, this.lastName);

  // Override hashCode using strategy from Effective Java,
  // Chapter 11.
  @override
  int get hashCode {
    int result = 17;
    result = 37 * result + firstName.hashCode;
    result = 37 * result + lastName.hashCode;
    return result;
  }

  // You should generally implement operator == if you
  // override hashCode.
  @override
  bool operator ==(dynamic other) {
    if (other is! Person) return false;
    Person person = other;
    return (person.firstName == firstName &&
        person.lastName == lastName);
  }
}

void main() {
  var p1 = Person('Bob', 'Smith');
  var p2 = Person('Bob', 'Smith');
  var p3 = 'not a person';
  assert(p1.hashCode == p2.hashCode);
  assert(p1 == p2);
  assert(p1 != p3);
}

noSuchMethod()

为检查或提醒代码是否调用了一个不存在的成员方法或成员变量, 可以通过重写 noSuchMethod()来实现:

class A {
  // Unless you override noSuchMethod, using a
  // non-existent member results in a NoSuchMethodError.
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}

枚举类型

  • 使用枚举

使用关键字 enum 关键字声明枚举类型:

enum Color { red, green, blue }

枚举类型有以0为开头递增下标 index, 默认值和下标一致:

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);

通过 values 获取所有枚举类型值:

List<Color> colors = Color.values;
assert(colors[2] == Color.blue);

将枚举类型用于 switch 表达式:

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // Without this, you see a WARNING.
    print(aColor); // 'Color.blue'
}

枚举类型限制:

  • 不可以子类化, 混合或者实现枚举
  • 不能显式实例化枚举

向类添加功能: mixins 混合

mixin 是复用多个类代码的一种方式. 只是代码的复用, 一般用于公共方法等, 比如接口层请求

通过 mixin 关键字实现混合. 含有显式构造方法的类不可以 mixin!!!

class A{
  A(){
    print('this is a');
  }
  aSay (){
    print('A said');
  }
}

class B{
//   B(){ //含有显式构造函数不能使用mixin
//     print('this is a');
//   }
  bSay (w){
    print('B said ${w}');
  }
}

mixin C {
  var what = 'what to say';
  cSay(){
    print('C said ${what}');
  }
}

class D extends A with B, C{
  dSay(){
    this.what = 'say hi, cnvoid';
    print('i am D ${what}');

    this.aSay();
  }
}

void main(){
  var d = D();
  d.aSay();
  d.bSay('cnvoid.com');
  d.cSay();
  d.dSay();
}

执行结果:


this is a
A said
B said cnvoid.com
C said what to say
i am D say hi, cnvoid
A said

实现一个mixin

通过使用 mixin 关键字代替 class 关键字实现mixin, 若要实现一个通用类并且可以mixin的类, 以然用class 关键字, 但不可以有显式的构造方法!

mxin 是Dart 2.1 语法, 更多信息请参考: https://github.com/dart-lang/language/blob/master/accepted/2.1/super-mixins/feature-specification.md#dart-2-mixin-declarations

类变量和方法

使用 static 关键字实现 类级别的变量和方法!

静态变量

静态变量(类变量) 通常用于 类级别状态管理和常量.

class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

-静态变量在使用的时候才被初始化

静态方法

静态方法不能操作实例对象, 因此没有this 的访问权限.

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);
}
  • 官方推荐在通用功能工具方法钟,考虑用全局函数代替静态方法

泛型 Generics

和C模板类, java 泛型基本一致

  • 正确指定泛型类型可生成更好的代码
  • 使用泛型减少代码量
  • 通用类型可以省去创建所有这些接口的麻烦
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error

使用列表文字

列表和映射是可以参数化的. 可以指定列表或者映射的类型:

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 names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = Set<String>.from(names);

var views = Map<int, View>();

验证泛型集合类型

在运行时, Dart依然带着类型信息

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true

-相反, 在Java中, 泛型使用擦除方式实现, 泛型参数类型将会被移除, 在Java中可以检测一个变量是 List 但不可以检测是否为 List< String >

隐式参数化类型

在实现泛型类型时, 如果需要限制类型, 使用 extends 关键字限制.

class Foo<T extends SomeBaseClass> {
  // Implementation goes here...
  String toString() => "Instance of 'Foo<$T>'";
}

class Extender extends SomeBaseClass {...}

SomeBaseClass 以及其任意子类可用于 Foo 泛型参数:

var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();

缺省泛型类型, 默认为 SomeBaseClass:

var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'

将泛型类型指定为其他类型将出现报错:

var foo = Foo<Object>();

使用泛型方法

Dart 不仅支持泛型类, 还支持泛型方法:

T first<T>(List<T> ts) {
  // Do some initial work or error checking, then...
  T tmp = ts[0];
  // Do some additional checking or processing...
  return tmp;
}

库和可视化

使用 import library 关键字创建一个模块化的, 可共享的代码库. 库不仅可以提供API, 更是一个隐私单元:以下划线开头的元素只能在当前库使用.

使用库

import URL;

import 'dart:html';

在使用内置库的时候, 以 dart: 开头. 对于其他库, 可以使用文件系统路径, 或者使用 package: 开头, 表示由包管理器提供的库.

import 'package:test/test.dart';

指定库前缀

如果引入的库有冲突, 可以为其中一个或都指定前缀. 或者说是为库设置别名. 下例子种两个库有相同的方法 Element(). 可以用下面方式指定前缀:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// Uses Element from lib1.
Element element1 = Element();

// Uses Element from lib2.
lib2.Element element2 = lib2.Element();

引入库部分

// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;

懒加载库(延迟加载)

  • 使用懒加载库, 首先需要使用 deferred as 关键字:
import 'package:greetings/hello.dart' deferred as hello;
  • 在使用懒加载库时:
Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

await关键字将暂停执行,直到hello库加载完成. 关于await async 访问 异步支持

  • 延迟加载库隐式插入当前命名空间
  • 延迟加载库的常量在加载完成前不可使用
  • 不可以使用延时加载库类型

实现库

在Dart生态中, 库可以创建和分发, 分为应用程序包和库包.

库包文件结构

root directory
             ├── lib
             |    ├── file1.dart
             │    └── file2.dart
             └── pubspec.yaml
  • 库的最小构成是,pubspec.yaml文件和lib文件夹

异步支持

在Dart库中,很多返回 Futrue 或者 Stream 对象的函数. 这些函数都是异步的. asyncawait 关键字支持异步编程, 让你的异步代码看起来和同步代码一样.

处理 Futrues

当你需要获取 Futures 的处理结果时, 可以有两种方式:

  • 使用 asyncawait
  • 使用 Futures 自带 API
await lookUpVersion();
Future checkVersion() async {
  var version = await lookUpVersion();
  // Do something with version
}