DartPad
DartPad
的出现让人眼前一亮,以后可以随时随地开心的测试一些代码了,Dart
的大部分语言功能DartPad
都支持。打开DartPad。
变量初始化
如果一个对象的引用不局限于单一的类型,可以根据设计指南将其指定为Object
或 dynamic
类型:
dynamic name1 = 'Jack';
Object name2 = 'Rose';
如果一个对象限于单一的类型:
var name1 = 'Jack';
String name2 = 'Rose';
局部变量声明的best practice
是使用var
来做类型被推断而非使用指定的类型。这样使用可以和其他大部分语言保持一致,提高可读性。
扩展操作符 ...
和 ...?
将一个 List 中的所有元素插入到另一个 List 中:
var list1 = [1, 2, 3];
var list2 = [0, ...list1];
assert(list2.length == 4);
如果扩展操作符右边可能为null
:
var list1;
var list2 = [0, ...?list1];
assert(list2.length == 1);
集合中使用控制流
使用Collection If
来创建一个List
:
var animals = [
'Dog',
'Cat',
'Fish',
if (canFly) 'Bird'
];
使用Collection For
将列表中的元素修改后添加到另一个列表中:
var listOfInts = [1, 2, 3];
var listOfStrings = [
0,
for (var i in listOfInts) i*i
];
print(listOfStrings); // [0, 1, 4, 9]
常量声明 final 和 const
-
const
是编译时常量,final
是运行时常量。二者都只能被赋值一次。 - 实例变量可以是
final
的但不可以是const
的,final
实例变量必须在构造器开始前被初始化,比如在声明实例变量时初始化,或者作为构造器参数,或者将其置于构造器的初始化列表中。 - 如果使用
const
修饰类中的变量,则必须加上static
关键字,即static const
。 - 没有使用
final
或const
修饰的变量的值是可以被更改的,即使这些变量之前引用过const
的值。
var list = const [];
list = [1, 2, 3];
-
Dart2.5
之后可以在定义常量时使用is
,as
,...
,...?
,collection if
,collection for
。
const Object i = 3;
const list = [i as int];
const map = {if (i is int) i: "int"};
const set = {if (list is List) ...list};
可选参数
- 命名参数:使用
{param1, param2, …}
的形式来指定命名参数。调用时需要指定对应的命名,也可以使用@required
来标识一个命名参数是必须的参数。使用@required
需要导入package:meta/meta.dart
。
void setUserInfo(String name, {@required int age, double height = 0, double weight = 0}) {
print('My name is $name, I am $age years old.');
if (height > 0) { print('My height is $height'); }
if (weight > 0) { print('My weight is $weight'); }
print('\n');
}
setUserInfo('Jack', age: 18);
setUserInfo('Rose', age: 18, height: 170);
My name is Jack, I am 18 years old.
My name is Rose, I am 18 years old.
My height is 170
- 位置参数:使用
[param1, param2, …]
的形式来指定位置参数。调用时需将位置对应起来。
void setUserInfo(String name, [int age, double height = 0, double weight = 0]) {
print('My name is $name, I am $age years old.');
if (height > 0) { print('My height is $height'); }
if (weight > 0) { print('My weight is $weight'); }
print('\n');
}
setUserInfo('Jack', 18);
setUserInfo('Rose', 18, 170, 105);
My name is Jack, I am 18 years old.
My name is Rose, I am 18 years old.
My height is 170
My weight is 105
级联调用
..
级联操作并非一个运算符而是Dart
的特殊语法。
querySelector('#confirm') // 获取对象
..text = 'Confirm' // 使用对象的成员
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
querySelector
返回了一个Selector
对象,后面的级联操作符都是调用这个Selector
对象的成员并忽略每个操作的返回值。上面的代码等价于:
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
在返回对象的函数中谨慎使用级联操作符:
var sb = StringBuffer();
sb.write('foo')
..write('bar'); // (Error: method 'write' isn't defined for 'void').
上述代码中的 sb.write() 方法返回的是 void,返回值为 void 的方法则不能使用级联运算符。
Getter 和 Setter
所有实例变量均会隐式地声明一个Getter
方法,非final
类型的实例变量还会隐式地声明一个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 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
可以让该类成为抽象类,抽象类将无法被实例化,常用于声明接口方法。
abstract class Animal {
void eat(); // 抽象方法。
}
class Dog extends Animal {
void eat() {
print('Dog eat!');
}
}
-
implements
为隐式接口,每一个类都隐式地定义了一个接口并实现了该接口,这个接口包含所有这个类的实例成员以及这个类所实现的其它接口。如果想要创建一个A
类支持调用B
类的API
且不想继承B
类,则可以实现B
类的接口。
class Point implements Comparable, Location {...}
-
extends
关键字创建一个子类,可使用super
关键字引用一个父类,可以使用@override
重写父类的实例方法。
class Television {
void turnOn() {
}
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
}
}
class SmartTelevision extends Television {
@override
void turnOn() {
}
}
-
Mixin
是一种在多重继承中复用某个类中代码的方法模式。使用with
关键字在其后跟上Mixin
类的名字来使用Mixin
模式。Mixin
类继承自Object
并且不为该类定义构造函数,可以使用关键字on
来指定哪些类可以使用该Mixin
类。
class Musician extends Performer with Musical {
}
class Maestro extends Person with Musical, Aggressive, Demented {
}
mixin MusicalPerformer on Musician {
}
库和可见性
-
as
用来指定前缀解决两个代码库有冲突的情况。比如如果library1
和library2
都有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();
-
show
和hide
可以有选择地导入代码库:
// 只导入 lib1 中的 foo。(Import only foo).
import 'package:lib1/lib1.dart' show foo;
// 导入 lib2 中除了 foo 外的所有。
import 'package:lib2/lib2.dart' hide foo;
-
deferred as
来标识需要延时加载的代码库:
import 'package:greetings/hello.dart' deferred as hello;
当实际需要使用到库中API
时先调用loadLibrary
函数加载库:
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}