• Dart 速查表 codelab
    • 字符串插值
      • 代码样例
    • 避空运算符
      • 代码样例
    • 条件属性访问
      • 代码样例
    • 集合字面量(Collection literals)
      • 代码样例
    • 箭头语法
      • 代码样例
    • 级连
      • 代码样例
    • Getters and setters
      • 代码样例
    • 可选位置参数
      • 代码样例
    • 可选命名参数
      • 代码样例
    • 异常
      • 代码样例
    • 在构造方法中使用 this
      • 代码样例
    • Initializer lists
      • 代码样例
    • 命名构造方法
      • 代码样例
    • 工厂构造方法
      • 代码样例
    • 重定向构造方法
      • 代码样例
    • Const 构造方法
      • 代码样例
    • 下一步是什么?

    Dart 速查表 codelab

    Dart 语言旨在让从其他编程语言转来的开发者们能够轻松学习,但也有它的独特之处。本篇将基于谷歌工程师编写的Dart 语言速查表为你介绍一些最重要的语言特性。

    在这篇 codelab 中的嵌入式编辑器已经完成了部分代码片段。你可以在这些编辑器上将代码补充完整,然后点击 Run (运行) 按钮进行测试。如果你需要帮助,请点击 Hint (提示) 按钮。要运行代码格式化 (dartfmt),点击 Format (格式化) 按钮,Reset (重置) 按钮将会清除你的操作,并把编辑器恢复到初始状态。

    备忘:

    本页面内嵌了一些 DartPads 做例子展示,

    如果你只看到了空白的框框(而没有任何内容),请查阅DartPad 常见问题页面。

    字符串插值

    为了将表达式的值放在字符串中,请使用 ${expression}。若表达式为单个标识符,则可以省略 {}

    下面是一些使用字符串插值的例子:

    字符串 结果
    '${3 + 2}' '5'
    '${"word".toUpperCase()}' 'WORD'
    '$myObject' myObject.toString() 的值

    代码样例

    下面的方法接收两个整形变量作为参数,然后让它返回一个包含以空格分隔的整数的字符串。例如,stringify(2, 3) 应该返回 '2 3'

    避空运算符

    Dart 提供了一系列方便的运算符用于处理可能会为空值的变量。其中一个是 ??= 赋值运算符,仅当该变量为空值时才为其赋值:

    1. int a; // The initial value of any object is null.
    2. a ??= 3;
    3. print(a); // <-- Prints 3.
    4.  
    5. a ??= 5;
    6. print(a); // <-- Still prints 3.

    另外一个避空运算符是 ??,如果该运算符左边的表达式返回的是空值,则会计算并返回右边的表达式。

    1. print(1 ?? 3); // <-- Prints 1.
    2. print(null ?? 12); // <-- Prints 12.

    代码样例

    尝试在下面使用 ??=?? 操作符。

    条件属性访问

    要保护可能会为空的属性的正常访问,请在点(.)之前加一个问号(?)。

    1. myObject?.someProperty

    上述代码等效于以下内容:

    1. (myObject != null) ? myObject.someProperty : null

    你可以在一个表达式中连续使用多个 ?.

    1. myObject?.someProperty?.someMethod()

    如果 myObjectmyObject.someProperty 为空,则前面的代码返回 null(并不再调用 someMethod)。

    代码样例

    尝试使用条件属性访问来完成下面的代码片段。

    集合字面量(Collection literals)

    Dart 内置了对 list、map 以及 set 的支持。你可以通过字面量直接创建它们:

    1. final aListOfStrings = ['one', 'two', 'three'];
    2. final aSetOfStrings = {'one', 'two', 'three'};
    3. final aMapOfStringsToInts = {
    4. 'one': 1,
    5. 'two': 2,
    6. 'three': 3,
    7. };

    Dart 的类型推断可以自动帮你分配这些变量的类型。在这个例子中,推断类型是 List<String>Set<String>Map<String, int>

    你也可以手动指定类型:

    1. final aListOfInts = <int>[];
    2. final aSetOfInts = <int>{};
    3. final aMapOfIntToDouble = <int, double>{};

    在使用子类型的内容初始化列表,但仍希望列表为 List <BaseType> 时,指定其类型很方便:

    1. final aListOfBaseType = <BaseType>[SubType(), SubType()];

    代码样例

    尝试将以下变量设定为指定的值。

    箭头语法

    你也许已经在 Dart 代码中见到过 => 符号。这种箭头语法是一种定义函数的方法,该函数将在其右侧执行表达式并返回其值。

    例如,考虑调用这个 List 类中的 any 方法:

    1. bool hasEmpty = aListOfStrings.any((s) {
    2. return s.isEmpty;
    3. });

    这里是一个更简单的代码实现:

    1. bool hasEmpty = aListOfStrings.any((s) => s.isEmpty);

    代码样例

    尝试使用箭头语法完成下面语句:

    级连

    要对同一对象执行一系列操作,请使用级联(..)。我们都看到过这样的表达式:

    1. myObject.someMethod()

    它在 myObject 上调用 someMethod 方法,而表达式的结果是 someMethod 的返回值。

    下面是一个使用级连语法的相同表达式:

    1. myObject..someMethod()

    虽然它仍然在 myObject 上调用了 someMethod,但表达式的结果却不是该方法返回值,而是是 myObject 对象的引用!使用级联,你可以将需要单独操作的语句链接在一起。例如,请考虑以下代码:

    1. var button = querySelector('#confirm');
    2. button.text = 'Confirm';
    3. button.classes.add('important');
    4. button.onClick.listen((e) => window.alert('Confirmed!'));

    使用级连能够让代码变得更加简洁,而且你也不再需要 button 变量了。

    1. querySelector('#confirm')
    2. ..text = 'Confirm'
    3. ..classes.add('important')
    4. ..onClick.listen((e) => window.alert('Confirmed!'));

    代码样例

    使用级联创建一个语句,分别将 BigObjectanInt 属性设为 1aString 属性设为 String!aList 属性设置为[3.0] 然后调用 allDone()

    Getters and setters

    任何需要对属性进行更多控制而不是允许简单字段访问的时候,你都可以自定义 getter 和 setter。

    例如,你可以用来确保属性值合法:

    1. class MyClass {
    2. int _aProperty = 0;
    3.  
    4. int get aProperty => _aProperty;
    5.  
    6. set aProperty(int value) {
    7. if (value >= 0) {
    8. _aProperty = value;
    9. }
    10. }
    11. }

    你还可以使用 getter 来定义计算属性:

    1. class MyClass {
    2. List<int> _values = [];
    3.  
    4. void addValue(int value) {
    5. _values.add(value);
    6. }
    7.  
    8. // A computed property.
    9. int get count {
    10. return _values.length;
    11. }
    12. }

    代码样例

    想象你有一个购物车类,其中有一个私有的 List<double> 类型的 prices 属性。添加以下内容:

    • 一个名为 total 的 getter,用于返回总价格。

    • 只要新列表不包含任何负价格,setter 就会用新的列表替换列表(在这种情况下,setter 应该抛出 InvalidPriceException)。

    可选位置参数

    Dart 有两种传参方法:位置参数和命名参数。位置参数你可能会比较熟悉:

    1. int sumUp(int a, int b, int c) {
    2. return a + b + c;
    3. }
    4.  
    5. int total = sumUp(1, 2, 3);

    在 Dart 中,你可以通过将这些参数包裹在大括号中使其变成可选位置参数:

    1. int sumUpToFive(int a, [int b, int c, int d, int e]) {
    2. int sum = a;
    3. if (b != null) sum += b;
    4. if (c != null) sum += c;
    5. if (d != null) sum += d;
    6. if (e != null) sum += e;
    7. return sum;
    8. }
    9.  
    10. int total = sumUptoFive(1, 2);
    11. int otherTotal = sumUpToFive(1, 2, 3, 4, 5);

    可选位置参数永远放在方法参数列表的最后。除非你给它们提供一个默认值,否则默认为 null。

    1. int sumUpToFive(int a, [int b = 2, int c = 3, int d = 4, int e = 5]) {
    2. ...
    3. }
    4.  
    5. int newTotal = sumUpToFive(1);
    6. print(newTotal); // <-- prints 15

    代码样例

    实现一个名为 joinWithCommas 的方法,它接收一至五个整数,然后返回由逗号分隔的包含这些数字的字符串。以下是方法调用和返回值的一些示例:

    方法调用 返回值
    joinWithCommas(1) '1'
    joinWithCommas(1, 2, 3) '1,2,3'
    joinWithCommas(1, 1, 1, 1, 1) '1,1,1,1,1'

    可选命名参数

    你可以使用大括号语法定义可选命名参数。

    1. void printName(String firstName, String lastName, {String suffix}) {
    2. print('$firstName $lastName ${suffix ?? ''}');
    3. }
    4.  
    5. printName('Avinash', 'Gupta');
    6. printName('Poshmeister', 'Moneybuckets', suffix: 'IV');

    正如你所料,这些参数默认为 null,但你也可以为其提供默认值。

    1. void printName(String firstName, String lastName, {String suffix = ''}) {
    2. print('$firstName $lastName ${suffix}');
    3. }

    一个方法不能同时使用可选位置参数和可选命名参数。

    代码样例

    MyDataObject 类添加一个 copyWith() 实例方法,它应该包含三个命名参数。

    • int newInt
    • String newString
    • double newDouble

    当该方法被调用时,copyWith 应该根据当前实例返回一个新的MyDataObject 并将前面参数(如果有的话)的数据复制到对象的属性中。例如,如果 newInt 不为空,则将其值复制到 anInt 中。

    异常

    Dart 代码可以抛出和捕获异常。与 Java 相比,Dart 的所有异常都是 unchecked exception。方法不会声明它们可能抛出的异常,你也不需要捕获任何异常。

    虽然 Dart 提供了 Exception 和 Error 类型,但是你可以抛出任何非空对象:

    1. throw Exception('Something bad happened.');
    2. throw 'Waaaaaaah!';

    使用 tryon 以及 catch 关键字来处理异常:

    1. try {
    2. breedMoreLlamas();
    3. } on OutOfLlamasException {
    4. // A specific exception
    5. buyMoreLlamas();
    6. } on Exception catch (e) {
    7. // Anything else that is an exception
    8. print('Unknown exception: $e');
    9. } catch (e) {
    10. // No specified type, handles all
    11. print('Something really unknown: $e');
    12. }

    try 关键字作用与其他大多数语言一样。使用 on 关键字按类型过滤特定异常,而 catch 关键字则能够获取捕捉到的异常对象的引用。

    如果你无法完全处理该异常,请使用 rethrow 关键字再次抛出异常:

    1. try {
    2. breedMoreLlamas();
    3. } catch (e) {
    4. print('I was just trying to breed llamas!.');
    5. rethrow;
    6. }

    要执行一段无论是否抛出异常都会执行的代码,请使用 finally

    1. try {
    2. breedMoreLlamas();
    3. } catch (e) {
    4. handle exception ...
    5. } finally {
    6. // Always clean up, even if an exception is thrown.
    7. cleanLlamaStalls();
    8. }

    代码样例

    在下面实现 tryFunction() 方法。它应该会执行一个不可靠的方法,然后做以下操作:

    • 如果 untrustworthy() 抛出了 ExceptionWithMessage,则调用 logger.logException 并传入使用异常类型和消息(尝试使用 oncatch)。

    • 如果 untrustworthy() 抛出了一个 Exception,则调用 logger.logException 并传入使用异常类型(这次请尝试使用 on)。

    • 如果 untrustworthy() 抛出了其他对象,请不要捕获该异常。

    • 捕获并处理完所有内容后,调用 logger.doneLogging(尝试使用 finally)。

    在构造方法中使用 this

    Dart 提供了一个方便的快捷方式,用于为构造方法中的属性赋值:在声明构造方法时使用 this.propertyName

    1. class MyColor {
    2. int red;
    3. int green;
    4. int blue;
    5.  
    6. MyColor(this.red, this.green, this.blue);
    7. }
    8.  
    9. final color = MyColor(80, 80, 128);

    此技巧同样也适用于命名参数。属性名为参数的名称:

    1. class MyColor {
    2. ...
    3.  
    4. MyColor({this.red, this.green, this.blue});
    5. }
    6.  
    7. final color = MyColor(red: 80, green: 80, blue: 80);

    对于可选参数,默认值为期望值:

    1. MyColor([this.red = 0, this.green = 0, this.blue = 0]);
    2. // or
    3. MyColor({this.red = 0, this.green = 0, this.blue = 0});

    代码样例

    使用 this 语法向 MyClass 添加一行构造方法,并接收和分配全部(三个)属性。

    Initializer lists

    有时,当你在实现构造函数时,您需要在构造函数体执行之前进行一些初始化。例如,final 修饰的字段必须在构造函数体执行之前赋值。在初始化列表中执行此操作,该列表位于构造函数的签名与其函数体之间:

    1. Point.fromJson(Map<String, num> json)
    2. : x = json['x'],
    3. y = json['y'] {
    4. print('In Point.fromJson(): ($x, $y)');
    5. }

    初始化列表也是放置断言的便利位置,它仅会在开发期间运行:

    1. NonNegativePoint(this.x, this.y)
    2. : assert(x >= 0),
    3. assert(y >= 0) {
    4. print('I just made a NonNegativePoint: ($x, $y)');
    5. }

    代码样例

    完成下面的 FirstTwoLetters 的构造函数。使用的初始化列表将 word 的前两个字符分配给 letterOneLetterTwo 属性。要获得额外的信用,请添加一个 断言 以捕获少于两个字符的单词。

    命名构造方法

    为了允许一个类具有多个构造方法,Dart 支持命名构造方法:

    1. class Point {
    2. num x, y;
    3.  
    4. Point(this.x, this.y);
    5.  
    6. Point.origin() {
    7. x = 0;
    8. y = 0;
    9. }
    10. }

    为了使用命名构造方法,请使用全名调用它:

    1. final myPoint = Point.origin();

    代码样例

    给 Color 类添加一个叫做 Color.black 的方法,它将会把三个属性的值都设为 0。

    工厂构造方法

    Dart 支持工厂构造方法。它能够返回其子类甚至 null 对象。要创建一个工厂构造方法,请使用 factory 关键字。

    1. class Square extends Shape {}
    2.  
    3. class Circle extends Shape {}
    4.  
    5. class Shape {
    6. Shape();
    7.  
    8. factory Shape.fromTypeName(String typeName) {
    9. if (typeName == 'square') return Square();
    10. if (typeName == 'circle') return Circle();
    11.  
    12. print('I don\'t recognize $typeName');
    13. return null;
    14. }
    15. }

    代码样例

    填写名为 IntegerHolder.fromList 的工厂构造方法,使其执行以下操作:

    • 若列表只有一个值,那么就用它来创建一个 IntegerSingle

    • 如果这个列表有两个值,那么按其顺序创建一个 IntegerDouble

    • 如果这个列表有三个值,那么按其顺序创建一个 IntegerTriple

    • 否则返回 null。

    重定向构造方法

    有时一个构造方法仅仅用来重定向到该类的另一个构造方法。重定向方法没有主体,它在冒号(:)之后调用另一个构造方法。

    1. class Automobile {
    2. String make;
    3. String model;
    4. int mpg;
    5.  
    6. // The main constructor for this class.
    7. Automobile(this.make, this.model, this.mpg);
    8.  
    9. // Delegates to the main constructor.
    10. Automobile.hybrid(String make, String model) : this(make, model, 60);
    11.  
    12. // Delegates to a named constructor
    13. Automobile.fancyHybrid() : this.hybrid('Futurecar', 'Mark 2');
    14. }

    代码样例

    还记得我们之前提到的 Color 类吗?创建一个叫做 black 的命名构造方法,但这次我们不要手动分配属性,而是将 0 作为参数,重定向到默认的构造方法。

    Const 构造方法

    如果你的类生成的对象永远都不会更改,则可以让这些对象成为编译时常量。为此,请定义 const 构造方法并确保所有实例变量都是 final 的。

    1. class ImmutablePoint {
    2. const ImmutablePoint(this.x, this.y);
    3.  
    4. final int x;
    5. final int y;
    6.  
    7. static const ImmutablePoint origin =
    8. ImmutablePoint(0, 0);
    9. }

    代码样例

    修改 Recipe 类,使其实例成为常量,并创建一个执行以下操作的常量构造方法:

    • 该方法有三个参数:ingredientscaloriesmilligramsOfSodium。(按照此顺序)

    • 使用 this 语法自动将参数值分配给同名的对象属性。

    • Recipe 的构造方法声明之前,用 const 关键字使其成为常量。

    下一步是什么?

    我们希望你能够喜欢这个 codelab 来学习或测试你对 Dart 语言中一些最有趣的功能的知识。下面是一些有关现在该做什么的建议:

    • 尝试阅读 其他 Dart codelab。

    • 阅读 Dart 语言之旅。

    • 在 DartPad 上进行练习。

    • 获取 Dart SDK。