由于准备入坑Flutter,所以Dart语言也是必学的。花了两天时间过了一遍官方入门文档,对于如果拥有其他面向对象语言基础,并且英文比较好的同学,上手Dart是非常快的(Swift,Dart,Kotlin三胞胎🤣)。本文总结了Dart的一些语法特点便于以后翻阅,比较基础的就跳过啦。
基本概念
- 所有的变量均为对象,所有的对象都是一个类的实例,包括数字,方法和空值(null)。所有的对象继承自Object类。
- Dart是强类型语言,并且可以推断变量类型即使没有声明。
- Dart无继承关键字(public, protected, and private),使用_开头的成员变量默认为私有属性。
变量
- 未初始化的变量默认为null,即使是数字这种基本类型也为null,因为在Dart中所有变量均为对象类型。
- 一个final修饰的变量无法再次被赋值。
- 一个const修饰的值在编译期间就已经确定,无法被改变。
内建类型
Dart对于以下数据类型有特殊处理:
- numbers
- strings
- booleans
- lists (also known as arrays)
- maps
- runes (for expressing Unicode characters in a string)
- symbols
可使用字面量语法初始化这些类型的对象
数字
Dart中的数字有两种类型:int和double
字符串转数字:
var one = int.parse('1');
数字转字符串:
String oneAsString = 1.toString();
(数字字面量直接用点语法,这糖真甜😁)
字符串
- Dart字符串为UTF-16字符序列,可使用””或者’’创建
- 使用
${expression}
将表达式放入一个字符串中 - 使用
==
判断两个字符串是否等同,若两个字符串的字符序列相同,则两者等同 - 使用
+
合并字符串 使用
r
创建一个字符串生值var s = r'In a raw string, not even \n gets special treatment.';
布尔值
- if和assert只可接收布尔值
数组
- 使用Lists表示,可自动推断元素类型
在数组前加const表示一个不可变数组,为编译器常量
var constantList = const [1, 2, 3];
字典
- 使用Maps表示,可自动推断元素类型
加const代表不可变字典
1
2
3
4
5final constantMap = const {
2: 'helium',
10: 'neon',
18: 'argon',
};
字符
- Dart字符由UFT-32字符集构成,由于字符(UFT-32)与字符串(UTF-16)为不用字符集,相互转化时需要特殊语法
- 表示一个Unicode字符:
\uXXXX
,超过4位:\u{1f600}
方法
- Dart中的方法是一个
Funciton
对象 Dart返回简写语法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
参数默认值
void enableFlags({bool bold = false, bool hidden = false}) {...}
- 函数可作为参数传入另一个函数
匿名函数
1
2
3
4var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
操作符
使用
as
做强制类型转换, 可能会抛出异常(emp as Person).firstName = 'Bob';
使用
is
判断对象类型1
2
3
4if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}expr1 ?? expr2
:若expr1非空,返回该值,否则返回expr2
级联调用
使用
..
对同一个对象连续做一系列操作,包括函数调用,无需声明临时变量1
2
3
4querySelector('#confirm') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
异常
- 未捕获的异常会造成程序终止
Dart提供
Exception
和Error
两种类型的异常,也可以抛出一个非空对象作为异常throw 'Out of llamas!';
使用on捕获特定类型的异常,使用catch捕获未被指定的异常
1
2
3
4
5
6
7
8
9
10
11
12try {
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');
}重复传播异常时使用
rethrow
关键字1
2
3
4
5
6
7
8
9void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // Allow callers to see the exception.
}
}使用
Finally
可以保证即使有异常产生也保证代码继续运行1
2
3
4
5
6try {
breedMoreLlamas();
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
类
使用
?.
可以避免调用者为空值时产生异常1
2// If p is non-null, set its y value to 4.
p?.y = 4;
构造函数
构造函数的语法糖:
1
2
3
4
5
6
7class Point {
num x, y;
// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}如果未声明构造函数,那么会隐式的生成一个无初始化参数的构造函数
- 构造函数不会被继承
命名构造函数,同样不会被继承
1
2
3
4
5
6
7
8
9
10
11class Point {
num x, y;
Point(this.x, this.y);
// Named constructor
Point.origin() {
x = 0;
y = 0;
}
}子类的构造函数默认会在首部调用父类的默认构造函数
若需要触发父类的非默认构造函数,则可在函数名后使用
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Person {
String firstName;
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');
}
}子类也可直接使用父类的构造函数
1
2
3
4class Employee extends Person {
Employee() : super.fromJson(getDefaultData());
// ···
}语法糖:可以在构造函数体调用之前出事偶会实例变量的值
1
2
3
4
5
6
7// 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)');
}语法糖:构造函数体调用前可使用断言验证参数合法性
1
2
3Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}重定向构造函数(使用已有的构造函数生成另一个构造函数)
1
2
3
4
5
6
7
8
9class 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);
}若对象不会被改变,可使用常量构造函数,需保证所有的实例变量均为编译期常量
1
2
3
4
5
6
7
8class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}工厂模式构造函数,如果调用构造函数时不一定需要生成一个实例时,可使用关键字
factory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class 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);
}
}
方法
- Dart会为对象的属性隐式生成getter和setter方法
- 抽象方法只可存在于抽象类中
- 抽象类不可被实例化,一般用于定义接口
声明抽象方法只需用
;
代替方法体1
2
3
4
5abstract class Doer {
// Define instance variables and methods...
void doSomething(); // Define an abstract method.
}类可以继承多个接口
使用
@override
重写成员函数1
2
3
4
5class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}支持操作符重写
- 重写
==
操作符时同时需要重写hashcode
方法(getter) - 重写
nosuchMethod
方法后,当调用一个不存在的方法时会被触发
枚举
使用
values
获取枚举所有值的数组1
2List<Color> colors = Color.values;
assert(colors[2] == Color.blue);使用switch语句时,如果没有处理所有的枚举值则会收到警告
- 枚举类型有如下限制:
- 不可继承,混入
- 不可实例化
混入
- 混入可在多个类的体系中复用一个类的代码
使用关键词
with
,支持混入多个类1
2
3
4
5
6
7
8
9
10
11class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}使用
on
指代一个混入类的特定类型1
2
3mixin MusicalPerformer on Musician {
// ···
}
类变量和类方法
- 使用
static
指代类变量和类方法 - 类变量不可访问
this
- 类变量是编译期常量,可作为函数参数传入
泛型
泛型类型是具体化的,运行时携带了完整的类型信息,可使用
is
判断其具体类型1
2
3var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true使用
extends
约束泛型类型1
2
3
4
5
6class Foo<T extends SomeBaseClass> {
// Implementation goes here...
String toString() => "Instance of 'Foo<$T>'";
}
class Extender extends SomeBaseClass {...}泛型函数:
- 返回值可为泛型 (T)
- 参数可为泛型 (List
) 临时变量为泛型 (T tmp)
1
2
3
4
5
6T 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;
}
库
使用
package:
做唯一标识import 'package:test/test.dart';
库名标识冲突时使用
as
1
2
3
4
5
6
7
8import '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();引入库的某一部分
1
2
3
4
5// 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;
当需要加载库时,调用
loadLibrary()
1
2
3
4Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}await
可保证库加载完成再执行后续代码loadLibrary()
可调用多次,但库只会被加载一次
异步
- 使用
async
和await
支持异步编程 - 要使用
await
,其方法须带有async
关键字 - 可以使用
try
,catch
和finally
来处理await
的异常 异步方法声明
1
2
3
4
5checkVersion() async {
// ...
}
lookUpVersion() async => /* ... */;异步方法在方法体执行之前就返回了
在循环中使用异步
1
2
3await for (variable declaration in expression) {
// Executes each time the stream emits a value.
}expression
返回的值必须是stream
类型的
生成器
- 当需要延迟产生一系列的值是使用
generator
,dart提供两种类型:- 同步-生成器,返回一个
Iterable
对象 - 异步-生成器,返回一个
Stream
对象
- 同步-生成器,返回一个
同步-生成器,方法体声明为
sync*
,使用yield
发送值1
2
3
4Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}异步-生成器,方法体声明为
async*
,使用yield
发送值1
2
3
4Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}使用
yield*
递归发送值1
2
3
4
5
6Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
Callable classes(可调用的类)
类实现
call()
方法后可作为函数被调用1
2
3
4
5
6
7
8
9class WannabeFunction {
call(String a, String b, String c) => '$a $b $c!';
}
main() {
var wf = new WannabeFunction();
var out = wf("Hi","there,","gang");
print('$out');
}
Isolates
Dart不使用多线程,所有的 Dart 代码在 isolates 中运行而不是线程。 每个 isolate 都有自己的堆内存,并且确保每个 isolate 的状态都不能被其他 isolate 访问