Dart 语言速查表
— 焉知非鱼Dart速查表。
字符串插值 #
使用 ${expression}
将表达式的值放到字符串里面。如果表达式是一个标识符, 就可以省略 {}
。
下面是字符串插值的例子:
字符串 | 结果 |
---|---|
‘${3 + 2}’ | ‘5’ |
‘${“word”.toUpperCase()}’ | ‘WORD’ |
‘$myObject’ | The value of myObject.toString() |
代码示例 #
下面的函数接收两个整数作为参数。使其返回一个包含两个整数的字符串,并以空格分隔。例如 stringify(2, 3)
应该返回 ‘2 3’。
String stringify(int x, int y) {
return '$x $y';
}
Null 无感知操作符 #
Dart 提供了一些方便的操作符来处理可能为空的值。其中一个是 ??=
赋值运算符,只有当一个变量当前为空时,它才会给这个变量赋值:
int a; // a 的初始值为 null
a ??= 3;
print(a); // 打印 3
a ??= 5;
print(a); // 仍然打印 3
另一个 null-aware 操作符是 ??
,它返回其左边的表达式,除非该表达式的值为 null,在这种情况下,它计算并返回其右边的表达式:
print(1 ?? 3); // 打印 1
print(null ?? 12); // 打印 12
代码示例 #
String foo = 'a string';
String bar; // Unassigned objects are null by default.
// makes 'a string' be assigned to baz.
String baz = foo ?? bar;
void updateSomeVars() {
// makes 'a string' be assigned to bar.
bar ??= 'a string';
}
有条件的属性访问 #
要保护对对象的一个可能为空的属性或方法的访问,请在点(.)前加上一个问号(?):
myObject?.someProperty
上述代码等同于以下代码:
(myObject != null) ? myObject.someProperty : null
你可以在一个表达式中把 ?.
的多个使用链接在一起:
myObject?.someProperty?.someMethod()
如果 myObject
或 myObject.someProperty
为 null,前面的代码将返回 null(并且从不调用 someMethod()
)。
代码示例 #
尝试使用条件属性访问来完成下面的代码片段。
// This method should return the uppercase version of `str`
// or null if `str` is null.
String upperCaseIt(String str) {
// Try conditionally accessing the `toUpperCase` method here.
return str?.toUpperCase();
}
集合字面量 #
Dart 内置了对列表、映射和集合的支持。你可以使用字面量创建它们:
final aListOfStrings = ['one', 'two', 'three'];
final aSetOfStrings = {'one', 'two', 'three'};
final aMapOfStringsToInts = {
'one': 1,
'two': 2,
'three': 3,
}
Dart 的类型推理可以为你分配类型给这些变量。在本例中,推断的类型是 List<String>
、Set<String>
和 Map<String, int>
。
或者你可以自己指定类型:
final aListOfInts = <int>[];
final aSetOfInts = <int>{};
final aMapOfIntToDouble = <int, double>{};
当你用子类型的内容初始化一个列表,但仍然希望列表是 List<BaseType>
时,指定类型是很方便的:
final aListOfBaseType = <BaseType>[SubType(), SubType()];
代码示例 #
尝试将以下变量设置为指定的值。
// Assign this a list containing 'a', 'b', and 'c' in that order:
final aListOfStrings = ['a', 'b', 'c'];
// Assign this a set containing 3, 4, and 5:
final aSetOfInts = {3, 4, 5};
// Assign this a map of String to int so that aMapOfStringsToInts['myKey'] returns 12:
final aMapOfStringsToInts = {'myKey': 12};
// Assign this an empty List<double>:
final anEmptyListOfDouble = <double>[];
// Assign this an empty Set<String>:
final anEmptySetOfString = <String>{};
// Assign this an empty Map of double to int:
final anEmptyMapOfDoublesToInts = <double, int>{};
箭头语法 #
你可能在 Dart 代码中看到过 =>
符号。这种箭头语法是一种定义函数的方式,该函数执行其右边的表达式并返回其值。
例如,考虑这个对 List
类的 any()
方法的调用:
bool hasEmpty = aListOfStrings.any((s) {
return s.isEmpty;
});
这里有一个更简单的方法来写这个代码:
bool hasEmpty = aListOfStrings.any((s) => s.isEmpty);
代码示例 #
试着完成以下使用箭头语法的语句:
class MyClass {
int _value1 = 2;
int _value2 = 3;
int _value3 = 5;
// Returns the product of the above values:
int get product => _value1 * _value2 * _value3;
// Adds one to _value1:
void incrementValue1() => _value1++;
// Returns a string containing each item in the
// list, separated by commas (e.g. 'a,b,c'):
String joinWithCommas(List<String> strings) => strings.join(',');
}
级联 #
要对同一对象进行一系列操作,可以使用级联(...
)。我们都见过这样的表达式:
myObject.someMethod()
它在 myObject
上调用 someMethod()
,表达式的结果是 someMethod()
的返回值。
下面是同样的表达式,有一个级联:
myObject..someMethod()
虽然它仍然在 myObject
上调用 someMethod()
,但表达式的结果并不是返回值-它是对 myObject
的引用! 使用级联,你可以将原本需要单独语句的操作串联起来。例如,请看以下代码:
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
有了级联,代码就会变得短得多,而且你也不需要 button
变量:
querySelector('#confirm')
..text = 'Confirm'
..class.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
代码示例 #
使用级联来创建一个单一的语句,将一个 BigObject
的 anInt
、aString
和 aList
属性设置为 1、‘String!’ 和 [3.0]
(分别地),然后调用 allDone()
。
class BigObject{
int anInt = 0;
String aString = '';
List<double> aList = [];
bool _done = false;
void allDone() {
_done = true;
}
}
BigObject fillBigObject(BigObject obj) {
return obj
..anInt = 1
..aString = 'String!'
..aList.add(3)
..allDone();
}
getters 和 setters #
当你需要对一个属性进行更多的控制时,你可以定义 getter 和 setter,而不是简单的字段。
例如,你可以确保一个属性的值是有效的:
class MyClass {
int _aProperty = 0;
int get aProperty => _aProperty;
set aProperty(int value) {
if (value >= 0) {
_aProperty = value;
}
}
}
你也可以使用 getter 来定义计算属性:
class MyClass {
List<int> _values = [];
void addValue(int value) {
_values.add(value);
}
// 一个计算属性
int get count {
return _values.length;
}
}
代码示例 #
想象一下,你有一个购物车类,它保存了一个私有的 List<double>
的价格。添加以下内容:
- 一个叫做
total
的 getter,返回价格的总和。 - 用一个新的列表替换列表的 setter,只要新的列表不包含任何负价格(在这种情况下,setter 应该抛出一个
InvalidPriceException
)。
class InvalidPriceException {}
class ShoppingCart {
List<double> _prices = [];
double get total => _prices.fold(0, (e, t) => e + t);
set prices(List<double> value) {
if (value.any((p) => p < 0)) {
throw InvalidPriceException();
}
_prices = value;
}
}
可选位置参数 #
Dart 有两种函数参数:位置参数和命名参数。位置参数是你可能熟悉的那种:
int sumUp(int a, int b, int c) {
return a + b + c;
}
// ...
int total = sumUp(1, 2, 3);
在 Dart 中,你可以将这些位置参数用括号包裹起来,使其成为可选的参数:
int sumUpToFive(int a, [int b, int c, int d, int e]) {
int sum = a;
if (b != null) sum += b;
if (c != null) sum += c;
if (d != null) sum += d;
if (e != null) sum += e;
return sum;
}
// ...
int total = sumUpToFive(1,2);
int otherTotal = sumUpToFive(1, 2, 3, 4, 5);
可选的位置参数在函数的参数列表中总是最后一个。它们的默认值是空的,除非你提供了另一个默认值:
int sumUpToFive(int a, [int b = 2, int c = 3, int d = 4, int e = 5]) {
// ···
}
// ···
int newTotal = sumUpToFive(1);
print(newTotal); // <-- prints 15
代码示例 #
实现一个名为 joinWithCommas()
的函数,接受 1 到 5 个整数,然后返回一个用逗号分隔的数字字符串。下面是一些函数调用和返回值的例子:
函数调用 | 返回值 |
---|---|
joinWithCommas(1) | ‘1’ |
joinWithCommas(1, 2, 3) | ‘1,2,3’ |
joinWithCommas(1, 1, 1, 1, 1) | ‘1,1,1,1,1’ |
main() {
var res = joinWithCommas(1,2,3,4);
print(res);
}
String joinWithCommas(int a, [int b, int c, int d, int e]) {
List<int> sum = [];
sum.add(a);
if (b != null) sum.add(b);
if (c != null) sum.add(c);
if (d != null) sum.add(d);
if (e != null) sum.add(e);
return sum.join(',');
}
可选命名参数 #
使用大括号语法,你可以定义有名称的可选参数。
void printName(String firstName, String lastName, {String suffix}) {
print('$firstName $lastName ${suffix ?? ''}');
}
// ···
printName('Avinash', 'Gupta');
printName('Poshmeister', 'Moneybuckets', suffix: 'IV');
正如你所期望的,这些参数的值默认为空,但你可以提供默认值。
void printName(String firstName, String lastName, {String suffix = ''}) {
print('$firstName $lastName $suffix');
}
一个函数不能同时拥有可选的位置参数和可选的命名参数。
代码示例 #
为 MyDataObject
类添加一个 copyWith()
实例方法。它应该接受三个命名参数:
- int newInt
- String newString
- double newDouble
当调用时,copyWith()
应该基于当前实例返回一个新的 MyDataObject
,并将前面参数(如果有的话)的数据复制到对象的属性中。例如,如果 newInt
是非空的,那么将其值复制到 anInt
中。
class MyDataObject {
final int anInt;
final String aString;
final double aDouble;
MyDataObject({
this.anInt = 1,
this.aString = 'Old!',
this.aDouble = 2.0,
});
MyDataObject copyWith({int newInt, String newString, double newDouble}) {
return MyDataObject(
anInt: newInt ?? this.anInt,
aString: newString ?? this.aString,
aDouble: newDouble ?? this.aDouble,
);
}
}
异常 #
Dart 代码可以抛出和捕获异常。与 Java 相比,Dart 的所有异常都是未检查的异常。方法不声明它们可能会抛出哪些异常,你也不需要捕捉任何异常。
Dart 提供了 Exception
和 Error
类型,但你可以抛出任何非空对象:
throw Exception('Something bad happened.');
throw 'Waaaaaaah!';
在处理异常时使用 try
、on
和 catch
关键字:
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');
}
try
关键字的工作原理和其他大多数语言一样。使用 on
关键字按类型过滤特定的异常,使用 catch
关键字获取异常对象的引用。
如果不能完全处理异常,可以使用 rethrow
关键字来传播异常:
try {
breedMoreLlamas();
} catch (e) {
print('I was just trying to breed llamas!.');
rethrow;
}
无论是否抛出异常,都要执行代码,使用 final
:
try {
breedMoreLlamas();
} catch (e) {
// ... handle exception ...
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
代码示例 #
实现下面的 tryFunction()
。它应该执行一个不可信的方法,然后做如下操作:
- 如果
untrustworthy()
抛出一个ExceptionWithMessage
,调用logger.logException
,并提供异常类型和消息(尝试使用on
和catch
)。 - 如果
untrustworthy()
抛出一个Exceptio
n,调用logger.logException
,并注明异常类型(尝试使用on
)。 - 如果
untrustworthy()
抛出任何其他对象,不要捕获异常。 - 当所有的东西都被捕获和处理后,调用
logger.doneLogging
(尝试使用finally
)。
typedef VoidFunction = void Function();
class ExceptionWithMessage {
final String message;
const ExceptionWithMessage(this.message);
}
abstract class Logger {
void logException(Type t, [String msg]);
void doneLogging();
}
void tryFunction(VoidFunction untrustworthy, Logger logger) {
try {
untrustworthy();
} on ExceptionWithMessage catch (e) {
logger.logException(e.runtimeType, e.message);
} on Exception {
logger.logException(Exception);
} finally {
logger.doneLogging();
}
}
在构造函数中使用 this
#
Dart 提供了一个方便的快捷方式来为构造函数中的属性赋值:在声明构造函数时使用 this.propertyName
:
class MyColor {
int red;
int green;
int blue;
MyColor(this.red, this.green, this.blue)
}
final color = MyColor(80, 80, 128);
这种技术也适用于命名参数。属性名成为参数的名称:
class MyColor {
...
MyColor({this.red, this.green, this.blue});
}
final color = MyColor(red: 80, green: 80, blue: 80);
对于可选参数,默认值按预期工作:
MyColor([this.red = 0, this.green = 0, this.blue = 0]);
// or
MyColor({this.red = 0, this.green = 0, this.blue = 0});
代码示例 #
为 MyClass
添加一个单行构造函数,使用 this.
语法来接收和分配类的三个属性的值:
class MyClass {
final int anInt;
final String aString;
final double aDouble;
MyClass(this.anInt, this.aString, this.aDouble);
}
初始化器列表 #
有时候,当你实现一个构造函数时,你需要在构造函数体执行之前做一些设置。例如,在构造函数体执行之前,final
字段必须有值。在一个初始化器列表中做这些工作,它位于构造函数的签名和它的主体之间。
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
初始化器列表也是一个方便放置断言的地方,它只在开发过程中运行:
NonNegativePoint(this.x, this.y)
: assert(x >= 0),
assert(y >= 0) {
print('I just made a NonNegativePoint: ($x, $y)');
}
代码示例 #
完成下面的 FirstTwoLetters
构造函数。使用初始化器列表将 word
中的前两个字符分配给 letterOne
和 LetterTwo
属性。为了获得额外的积分,可以添加一个断言来捕获少于两个字符的单词。
class FirstTwoLetters {
final String letterOne;
final String letterTwo;
// Create a constructor with an initializer list here:
FirstTwoLetters(String word)
: assert(word.length >=2),
letterOne = word[0],
letterTwo = word[1];
}
命名构造器 #
为了允许类有多个构造函数,Dart 支持命名构造函数:
class Point {
double x, y;
Point(this.x, this.y);
Point.origin() {
x = 0;
y = 0;
}
}
要使用命名构造函数,请使用它的全名来调用它:
final myPoint = Point.origin();
代码示例 #
给 Color
类一个名为 Color.black
的构造函数,将三个属性都设置为 0。
class Color {
int red;
int green;
int blue;
Color(this.red, this.green, this.blue);
Color.black() {
red = 0;
green = 0;
blue = 0;
}
}
工厂构造函数 #
Dart 支持工厂构造函数,它可以返回子类型甚至 null。要创建一个工厂构造函数,请使用 factory
关键字:
class Square extends Shape {}
class Circle extends Shape {}
class Shape {
Shape();
factory Shape.fromTypeName(String typeName) {
if (typeName == 'square') return Square();
if (typeName == 'circle') return Circle();
print('I don\'t recognize $typeName');
return null
}
}
代码示例 #
填入名为 IntegerHolder.fromList
的工厂构造函数,使其做以下工作:
- 如果列表有一个值,就用这个值创建一个
IntegerSingle
。 - 如果列表有两个值,则用该值依次创建一个
IntegerDouble
。 - 如果列表有三个值,则按顺序创建一个
IntegerTriple
。 - 否则,返回 null。
class IntegerHolder {
IntegerHolder();
factory IntegerHolder.fromList(List<int> list) {
if (list?.length == 1) {
return IntegerSingle(list[0]);
} else if (list?.length == 2) {
return IntegerDouble(list[0], list[1]);
} else if (list?.length == 3) {
return IntegerTriple(list[0], list[1], list[2]);
} else {
return null;
}
}
}
class IntegerSingle extends IntegerHolder {
final int a;
IntegerSingle(this.a);
}
class IntegerDouble extends IntegerHolder {
final int a;
final int b;
IntegerDouble(this.a, this.b);
}
class IntegerTriple extends IntegerHolder {
final int a;
final int b;
final int c;
IntegerTriple(this.a, this.b, this.c);
}
重定向构造函数 #
有时,一个构造函数的唯一目的是重定向到同一类中的另一个构造函数。重定向构造函数的主体是空的,构造函数调用出现在冒号(:)之后。
class Automobile {
String make;
String model;
int mpg;
// 这个类的主构造函数
Automobile(this.make, this.model, this.mpg);
// 代理到主构造函数
Automobile.hybrid(String make, String model) : this(make, model, 60);
// 代理到命名构造函数
Automobile.fancyHybrid() : this.hybrid('Futurecar', 'Mark 2');
}
代码示例 #
还记得上面的 Color
类吗?创建一个名为 black
的命名构造函数,但不是手动分配属性,而是将其重定向到默认构造函数,参数为 0。
class Color {
int red;
int green;
int blue;
Color(this.red, this.green, this.blue);
Color.black() : this(0, 0, 0);
}
常量构造函数 #
如果你的类产生的对象永远不会改变,你可以让这些对象成为编译时常量。要做到这一点,请定义一个 const
构造函数,并确保所有的实例变量都是最终变量。
class ImmutablePoint {
const ImmutablePoint(this.x, this.y);
final int x;
final int y;
static const ImmutablePoint origin = ImmutablePoint(0, 0);
}
代码示例 #
修改 Recipe
类,使它的实例可以是常量,并创建一个常量构造函数,执行以下操作。
- 有三个参数:
ingredients
,calories
和milligramsOfSodium
(按顺序)。 - 使用
this.
语法,自动将参数值分配给同名的对象属性。 - 是常量,在构造函数声明中,
const
关键字就在Recipe
前面。
class Recipe {
final List<String> ingredients;
final int calories;
final double milligramsOfSodium;
const Recipe(this.ingredients, this.calories, this.milligramsOfSodium);
}
下一步是什么? #
我们希望你喜欢使用这个 codelab 来学习或测试你对 Dart 语言一些最有趣的功能的知识。这里有一些关于现在要做什么的建议。
- 试试其他的 Dart 代码实验室.
- 阅读 Dart 语言之旅。
- 玩 DartPad。
- 获取 Dart SDK。