dart-lang
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值不变
- 复合赋值运算符
复合赋值 | 等价表达式 | |
---|---|---|
运算符 op | a op b | a = a op b |
例如: | a~/= b | a = a ~/ b |
- 逻辑运算符: && - 与; || - 或; ! - 非
- 位运算符
Operator | Meaning |
---|---|
& | AND |
| | OR |
^ | XOR |
~expr | Unary 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;
}
}
- 调用没有默认父类的构造方法
执行顺序如下:
- 构造器列表
- 父类梅参数构造器
- 当前类无参构造器执行
**如果父类没有未命名没有参数的构造函数, 必须手动调用父类的一个构造函数. 调用方式是在当前构造函数体前用(:)调用:
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 对象的函数. 这些函数都是异步的. async 和 await 关键字支持异步编程, 让你的异步代码看起来和同步代码一样.
处理 Futrues
当你需要获取 Futures 的处理结果时, 可以有两种方式:
- 使用 async 和 await
- 使用 Futures 自带 API
await lookUpVersion();
Future checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}