AndroidDart从基础到进阶Dart

Dart语法学习

2018-07-29  本文已影响8704人  GuoDongW

目录

参考资料

语言特性

关键字(56个)

关键字 - - -
abstract do import super
as dynamic in switch
assert else interface sync*
enum implements is this
async* export library throw
await external mixin true
break extends new try
case factory null typedef
catch false operator var
class final part void
const finally rethrow while
continue for return with
covariant get set yield*
default if static deferred

变量与常量

  1. 变量声明与初始化
  // 没有明确类型,编译的时候根据值明确类型
  var name = ‘Bob’; 
  Object name = '张三';
  dynamic name = '李四';

  // 显示声明将被推断类型, 可以使用String显示声明字符串类型
  String name = 'Bob' ;
  
  1. 默认值
  //测试 数字类型的初始值是什么?
  int lineCount;
  // 为false的时候抛出异常
  assert(lineCount == null);
  print(lineCount); //打印结果为null,证明数字类型初始化值是null
  
  1. final and const

    • 如果您从未打算更改一个变量,那么使用 final 或 const,不是var,也不是一个类型。
      一个 final 变量只能被初始化一次; const变量是一个编译时常量,(Const变量是隐式的final)
      final的顶级或类变量在第一次使用时被初始化。

    • 被final修饰的顶级变量或类变量在第一次声明的时候就需要初始化。

    // The final variable 'outSideFinalName' must be initialized.
    final String outSideFinalName
    
    
    • 被final或者const修饰的变量,变量类型可以省略,建议指定数据类型。
     //可以省略String这个类型声明
     final name = "Bob";
     final String name1  = "张三";
     
     const name2 = "alex";
     const String name3 = "李四";
    
    
    • 被 final 或 const 修饰的变量无法再去修改其值。
     final String outSideFinalName = "Alex";
     // outSideFinalName', a final variable, can only be set once
     // 一个final变量,只能被设置一次。
     outSideFinalName = "Bill";
     
     const String outSideName = 'Bill';
     // 这样写,编译器提示:Constant variables can't be assigned a value
     // const常量不能赋值
     // outSideName = "小白";
    
    
    • flnal 或者 const 不能和 var 同时使用
     // Members can't be declared to be both 'const' and 'var'
     const var String outSideName = 'Bill';
     
     // Members can't be declared to be both 'final' and 'var'
     final var String name = 'Lili';
    
    
    • 常量如果是类级别的,请使用 static const
     // 常量如果是类级别的,请使用 static const
     static const String name3 = 'Tom';
     
     // 这样写保存
     // Only static fields can be declared as const
     // 只有静态字段可以声明为const
     //const String name3 = 'Tom';
    
    
    • 常量的运算
     const speed = 100; //速度(km/h)
     const double distance = 2.5 * speed; // 距离 = 时间 * 速度
    
     final speed2 = 100; //速度(km/h)
     final double distance2 = 2.5 * speed2; // 距离 = 时间 * 速度
     
    
    • const关键字不只是声明常数变量,您也可以使用它来创建常量值,以及声明创建常量值的构造函数,任何变量都可以有一个常量值。
     // 注意: [] 创建的是一个空的list集合
     // const []创建一个空的、不可变的列表(EIL)。
     var varList = const []; // varList 当前是一个EIL
     final finalList = const []; // finalList一直是EIL
     const constList = const []; // constList 是一个编译时常量的EIL
    
     // 可以更改非final,非const变量的值
     // 即使它曾经具有const值
     varList = ["haha"];
    
     // 不能更改final变量或const变量的值
     // 这样写,编译器提示:a final variable, can only be set once
     // finalList = ["haha"];
     // 这样写,编译器提示:Constant variables can't be assigned a value  
     // constList = ["haha"];
    
    
    • 在常量表达式中,该运算符的操作数必须为'bool'、'num'、'String'或'null', const常量必须用conat类型的值初始化。
    
    const String outSideName = 'Bill';
    final String outSideFinalName = 'Alex';
    const String outSideName2 = 'Tom';
    
    const aConstList = const ['1', '2', '3'];
    
    // In constant expressions, operands of this operator must be of type 'bool', 'num', 'String' or 'null'
    // 在常量表达式中,该运算符的操作数必须为'bool'、'num'、'String'或'null'。
    const validConstString = '$outSideName $outSideName2 $aConstList';
    
    // Const variables must be initialized with a constant value
    // const常量必须用conat类型的值初始化
    const validConstString = '$outSideName $outSideName2 $outSideFinalName';
    
    var outSideVarName='Cathy';
    // Const variables must be initialized with a constant value.
    // const常量必须用conat类型的值初始化
    const validConstString = '$outSideName $outSideName2 $outSideVarName';
    
    // 正确写法
    const String outSideConstName = 'Joy';
    const validConstString = '$outSideName $outSideName2 $outSideConstName';
    
    

数据类型

  1. num

    • num 是数字类型的父类,有两个子类 int 和 double。

    • int 根据平台的不同,整数值不大于64位。在Dart VM上,值可以从-263到263 - 1,编译成JavaScript的Dart使用JavaScript代码,允许值从-253到253 - 1。

    • double 64位(双精度)浮点数,如IEEE 754标准所规定。

     int a = 1;
     print(a);
     
     double b = 1.12;
     print(b);
     
     // String -> int
     int one = int.parse('1');
     // 输出3
     print(one + 2);
     
     // String -> double
     var onePointOne = double.parse('1.1');
     // 输出3.1
     print(onePointOne + 2);
    
     // int -> String
     String oneAsString = 1.toString();
     // The argument type 'int' can't be assigned to the parameter type 'String'
     //print(oneAsString + 2);
     // 输出 1 + 2
     print('$oneAsString + 2');
     // 输出 1 2
     print('$oneAsString 2');
    
     // double -> String 注意括号中要有小数点位数,否则报错
     String piAsString = 3.14159.toStringAsFixed(2);
     // 截取两位小数, 输出3.14
     print(piAsString);
     
     String aString = 1.12618.toStringAsFixed(2);
     // 检查是否四舍五入,输出1.13,发现会做四舍五入
     print(aString);
    
    
  2. String

    • Dart里面的String是一系列 UTF-16 代码单元。
    • 您可以使用单引号或双引号来创建一个字符串。
    • 单引号或者双引号里面嵌套使用引号。
    • {} 来计算字符串中变量的值,需要注意的是如果是表达式需要${表达式}
     String singleString = 'abcdddd';
     String doubleString = "abcsdfafd";
     
     String sdString = '$singleString a "bcsd" ${singleString}';
     String dsString = "abc 'aaa' $sdString";
     print(sdString);
     print(dsString);
    
    
     String singleString = 'aaa';
     String doubleString = "bbb";
     // 单引号嵌套双引号
     String sdString = '$singleString a "bbb" ${doubleString}';
     // 输出 aaa a "bbb" bbb
     print(sdString);
     
     // 双引号嵌套单引号
     String dsString = "${singleString.toUpperCase()} abc 'aaa' $doubleString.toUpperCase()";
     // 输出 AAA abc 'aaa' bbb.toUpperCase(), 
     可以看出 ”$doubleString.toUpperCase()“ 没有加“{}“,导致输出结果是”bbb.toUpperCase()“
     print(dsString);
     
    
  3. bool

  // 检查是否为空字符串
  var fullName = '';
  assert(fullName.isEmpty);

  // 检查0
  var hitPoints = 0;
  assert(hitPoints <= 0);

  // 检查是否为null
  var unicorn;
  assert(unicorn == null);

  // 检查是否为NaN
  var iMeantToDoThis = 0 / 0;
  assert(iMeantToDoThis.isNaN);

  1. List集合
 //创建一个int类型的list
 List list = [10, 7, 23];
 // 输出[10, 7, 23]
 print(list);
 
 // 使用List的构造函数,也可以添加int参数,表示List固定长度,不能进行添加 删除操作
 var fruits = new List();
 
 // 添加元素
 fruits.add('apples');
 
 // 添加多个元素
 fruits.addAll(['oranges', 'bananas']);
 
 List subFruits = ['apples', 'oranges', 'banans'];
 // 添加多个元素
 fruits.addAll(subFruits);
 
 // 输出: [apples, oranges, bananas, apples, oranges, banans]
 print(fruits);
 
 // 获取List的长度
 print(fruits.length);
 
 // 获取第一个元素
 print(fruits.first);
 
 // 获取元素最后一个元素
 print(fruits.last);
 
 // 利用索引获取元素
 print(fruits[0]);
 
 // 查找某个元素的索引号
 print(fruits.indexOf('apples'));
 
 // 删除指定位置的元素,返回删除的元素
 print(fruits.removeAt(0));

 // 删除指定元素,成功返回true,失败返回false
 // 如果集合里面有多个“apples”, 只会删除集合中第一个改元素
 fruits.remove('apples');

 // 删除最后一个元素,返回删除的元素
 fruits.removeLast();

 // 删除指定范围(索引)元素,含头不含尾
 fruits.removeRange(start,end);

 // 删除指定条件的元素(这里是元素长度大于6)
 fruits.removeWhere((item) => item.length >6);

 // 删除所有的元素
 fruits.clear();

  1. Map集合
 // Two keys in a map literal can't be equal.
 // Map companys = {'Alibaba': '阿里巴巴', 'Tencent': '腾讯', 'baidu': '百度', 'Alibaba': '钉钉', 'Tenect': 'qq-music'};
 
 Map companys = {'Alibaba': '阿里巴巴', 'Tencent': '腾讯', 'baidu': '百度'};
 // 输出:{Alibaba: 阿里巴巴, Tencent: 腾讯, baidu: 百度}
 print(companys);

 Map schoolsMap = new Map();
 schoolsMap['first'] = '清华';
 schoolsMap['second'] = '北大';
 schoolsMap['third'] = '复旦';
 // 打印结果 {first: 清华, second: 北大, third: 复旦}
 print(schoolsMap);

 var fruits = new Map();
 fruits["first"] = "apple";
 fruits["second"] = "banana";
 fruits["fifth"] = "orange";
 //换成双引号,换成var 打印结果 {first: apple, second: banana, fifth: orange}
 print(fruits);
    
// 指定键值对的参数类型
var aMap = new Map<int, String>();

// Map的赋值,中括号中是Key,这里可不是数组
aMap[1] = '小米';

//Map中的键值对是唯一的
//同Set不同,第二次输入的Key如果存在,Value会覆盖之前的数据
aMap[1] = 'alibaba';

// map里面的value可以相同
aMap[2] = 'alibaba';

// map里面value可以为空字符串
aMap[3] = '';

// map里面的value可以为null
aMap[4] = null;

print(aMap);

// 检索Map是否含有某Key
assert(aMap.containsKey(1));

//删除某个键值对
aMap.remove(1); 

print(aMap);  

运算符

描述 操作符
一元后置操作符 expr++ expr-- () [] . ?.
一元前置操作符 expr !expr ~expr ++expr --expr
乘除 * / % ~/
加减 + -
位移 << >>
按位与 &
按位或
按位异或 ^
逻辑与 &&
逻辑或
关系和类型判断 >= > <= < as is is!
== !=
如果为空 ??
条件表达式 expr1 ? expr2 : expr3
赋值 = *= /= ~/= %= += -= <<= >>= &= ^= = ??=
级联 ..

流程控制语句(Control flow statements)

异常(Exceptions)

  1. throw
  throw new FormatException('Expected at least 1 section');

  1. 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, s) {
    print('Exception details:\n $e');
    print('Stack trace:\n $s');
 }

  1. rethrow

 final foo = '';

 void misbehave() {
    try {
      foo = "1";
    } catch (e) {
      print('2');
      rethrow;// 如果不重新抛出异常,main函数中的catch语句执行不到
    }
 }

 void main() {
    try {
      misbehave();
    } catch (e) {
      print('3');
    }
 }

  1. finally

    • Dart的finally用来执行那些无论异常是否发生都执行的操作。
      final foo = '';
    
      void misbehave() {
        try {
          foo = "1";
        } catch (e) {
          print('2');
        }
      }
    
      void main() {
        try {
          misbehave();
        } catch (e) {
          print('3');
        } finally {
          print('4'); // 即使没有rethrow最终都会执行到
        }
      }
    
    

函数 Function

  bool isNoble(int atomicNumber) {
     return _nobleGases[atomicNumber] != null;
  }     

  1. main()函数

    • 每个应用程序都必须有一个顶层main()函数,它可以作为应用程序的入口点。该main()函数返回void并具有List<String>参数的可选参数。
    void main() {
       querySelector('#sample_text_id')
           ..text = 'Click me!'
           ..onClick.listen(reverseText);
    }
    
    
    • 级联符号..允许您在同一个对象上进行一系列操作。除了函数调用之外,还可以访问同一对象上的字段。这通常会为您节省创建临时变量的步骤,并允许您编写更流畅的代码。
    querySelector('#confirm') // Get an object.
        ..text = 'Confirm' // Use its members.
        ..classes.add('important')
        ..onClick.listen((e) => window.alert('Confirmed!'));
    
    
    • 上述例子相对于:
     var button = querySelector('#confirm');
     button.text = 'Confirm';
     button.classes.add('important');
     button.onClick.listen((e) => window.alert('Confirmed!'));
    
    
    • 级联符号也可以嵌套使用。 例如:
      final addressBook = (AddressBookBuilder()
       ..name = 'jenny'
       ..email = 'jenny@example.com'
       ..phone = (PhoneNumberBuilder()
             ..number = '415-555-0100'
             ..label = 'home')
           .build())
       .build();
       
    
    • 当返回值是void时不能构建级联。 例如,以下代码失败:
    var sb = StringBuffer();
    sb.write('foo') // 返回void
      ..write('bar'); // 这里会报错
    
    
    • 注意: 严格地说,级联的..符号不是操作符。它只是Dart语法的一部分。
  1. 可选参数

    • 可选的命名参数, 定义函数时,使用{param1, param2, …},用于指定命名参数。例如:
     //设置[bold]和[hidden]标志
     void enableFlags({bool bold, bool hidden}) {
          // ... 
     }  
     
     enableFlags(bold: true, hidden: false);
    
    
    • 可选的位置参数,用[]它们标记为可选的位置参数:
      String say(String from, String msg, [String device]) {
          var result = '$from says $msg';
          if (device != null) {
             result = '$result with a $device';
          }
          return result;
      }
    
    
    • 下面是一个不带可选参数调用这个函数的例子:
      say('Bob', 'Howdy'); //结果是: Bob says Howdy
    
    
    • 下面是用第三个参数调用这个函数的例子:
      say('Bob', 'Howdy', 'smoke signal'); //结果是:Bob says Howdy with a smoke signal
    
    
  2. 默认参数

    • 函数可以使用=为命名参数和位置参数定义默认值。默认值必须是编译时常量。如果没有提供默认值,则默认值为null。

    • 下面是为命名参数设置默认值的示例:

      // 设置 bold 和 hidden 标记的默认值都为false
      void enableFlags2({bool bold = false, bool hidden = false}) {
           // ...
      }
    
      // 调用的时候:bold will be true; hidden will be false.
      enableFlags2(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;
     }
    
     //调用方式:
     say('Bob', 'Howdy'); //结果为:Bob says Howdy with a carrier pigeon;
    
    
    • 您还可以将list或map作为默认值传递。下面的示例定义一个函数doStuff(),该函数指定列表参数的默认list和gifts参数的默认map。
     // 使用list 或者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');
     }
    
    
  3. 作为一个类对象的功能


  void printElement(int element) {
     print(element);
  }

  var list = [1, 2, 3];

  // 把 printElement函数作为一个参数传递进来
  list.forEach(printElement);


  var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
  assert(loudify('hello') == '!!! HELLO !!!');

  1. 匿名函数

    • 大多数函数都能被命名为匿名函数,如 main() 或 printElement()。您还可以创建一个名为匿名函数的无名函数,有时也可以创建lambda或闭包。您可以为变量分配一个匿名函数,例如,您可以从集合中添加或删除它。

    • 一个匿名函数看起来类似于一个命名函数 - 0或更多的参数,在括号之间用逗号和可选类型标注分隔。

    • 下面的代码块包含函数的主体:

    
      ([[Type] param1[, …]]) { 
         codeBlock; 
      }; 
    
    
    • 下面的示例定义了一个具有无类型参数的匿名函数item,该函数被list中的每个item调用,输出一个字符串,该字符串包含指定索引处的值。
    
     var list = ['apples', 'bananas', 'oranges'];
     list.forEach((item) {
        print('${list.indexOf(item)}: $item');
     });
    
    
    • 如果函数只包含一条语句,可以使用箭头符号=>来缩短它, 比如上面的例2可以简写成:
    
     list.forEach((item) => print('${list.indexOf(item)}: $item'));
    
    
  2. 返回值


  foo() {}
  assert(foo() == null);
  

类(Classes)

  1. 对象

  var jsonData = JSON.decode('{"x":1, "y":2}');

  // Create a Point using Point().
  var p1 = new Point(2, 2);

  // Create a Point using Point.fromJson().
  var p2 = new Point.fromJson(jsonData);



  var p = new 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(new Point(4, 4));

```

 // If p is non-null, set its y value to 4.
 p?.y = 4;

```
```

 var p = const ImmutablePoint(2, 2);

``` 
```

 print('The type of a is ${a.runtimeType}');

```
  1. 实例化变量(Instance variables)

 class Point {
    num x; // Declare instance variable x, initially null.
    num y; // Declare y, initially null.
    num z = 0; // Declare z, initially 0.
 }

 class Point {
    num x;
    num y;
 }

 main() {
    var point = new Point();
    point.x = 4;          // Use the setter method for x.
    assert(point.x == 4); // Use the getter method for x.
    assert(point.y == null); // Values default to null.
 }

  1. 构造函数(Constructors)
  class Point {
     num x;
     num y;

     Point(num x, num y) {
        // There's a better way to do this, stay tuned.
        this.x = x;
        this.y = y;
     }
  }

 class Point {
    num x;
    num y;

    // Syntactic sugar for setting x and y
    // before the constructor body runs.
    Point(this.x, this.y);
 }

  1. 构造函数不能继承(Constructors aren’t inherited)
  1. 命名的构造函数(Named constructors)
class Point {
   num x;
   num y;

   Point(this.x, this.y);

   // 命名构造函数Named constructor
   Point.fromJson(Map json) {
     x = json['x'];
     y = json['y'];
   }
}

  1. 调用父类的非默认构造函数
  1. 初始化列表

    • 除了调用父类的构造函数,也可以通过初始化列表在子类的构造函数体前(大括号前)来初始化实例的变量值,使用逗号,分隔。如下所示:
    class Point {
       num x;
       num y;
    
       Point(this.x, this.y);
    
       // 初始化列表在构造函数运行前设置实例变量。
       Point.fromJson(Map jsonMap)
       : x = jsonMap['x'],
         y = jsonMap['y'] {
          print('In Point.fromJson(): ($x, $y)');
       }
     }
    
    

    注意:上述代码,初始化程序无法访问 this 关键字。

  2. 静态构造函数

    • 如果你的类产生的对象永远不会改变,你可以让这些对象成为编译时常量。为此,需要定义一个 const 构造函数并确保所有的实例变量都是 final 的。
    
    class ImmutablePoint {
        final num x;
        final num y;
        const ImmutablePoint(this.x, this.y);
        static final ImmutablePoint origin = const ImmutablePoint(0, 0);
    }
    
    
  3. 重定向构造函数

    • 有时候构造函数的目的只是重定向到该类的另一个构造函数。重定向构造函数没有函数体,使用冒号:分隔。
    class Point {
        num x;
        num y;
    
        // 主构造函数
        Point(this.x, this.y) {
            print("Point($x, $y)");
        }
    
        // 重定向构造函数,指向主构造函数,函数体为空
        Point.alongXAxis(num x) : this(x, 0);
    }
    
    void main() {
        var p1 = new Point(1, 2);
        var p2 = new Point.alongXAxis(4);
    }
    
    
  4. 常量构造函数

class ImmutablePoint {
   final num x;
   final num y;
   const ImmutablePoint(this.x, this.y);
   static final ImmutablePoint origin = const ImmutablePoint(0, 0);
}

  1. 工厂构造函数

class Logger {
   final String name;
   bool mute = false;

   // _cache 是一个私有库,幸好名字前有个 _ 。 
   static final Map<String, Logger> _cache = <String, Logger>{};

   factory Logger(String name) {
       if (_cache.containsKey(name)) {
          return _cache[name];
       } else {
          final logger = new Logger._internal(name);
          _cache[name] = logger;
          return logger;
       }
    }

    Logger._internal(this.name);

    void log(String msg) {
       if (!mute) {
          print(msg);
       }
    }
    
 }

注意:工厂构造函数不能用 this。

方法

  1. 实例方法

    • 对象的实例方法可以访问实例变量和 this 。以下示例中的 distanceTo() 方法是实例方法的一个例子:
    import 'dart:math';
    
    class Point {
       num x;
       num 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);
       }
     }
    
    
  2. setters 和 Getters

    • 是一种提供对方法属性读和写的特殊方法。每个实例变量都有一个隐式的 getter 方法,合适的话可能还会有 setter 方法。你可以通过实现 getters 和 setters 来创建附加属性,也就是直接使用 get 和 set 关键词:
    class Rectangle {
       num left;
       num top;
       num width;
       num height;
    
       Rectangle(this.left, this.top, this.width, this.height);
    
       // 定义两个计算属性: 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;
    }
    
    main() {
       var rect = new Rectangle(3, 4, 20, 15);
       assert(rect.left == 3);
       rect.right = 12;
       assert(rect.left == -8);
    }
    
    
    • 借助于 getter 和 setter ,你可以直接使用实例变量,并且在不改变客户代码的情况下把他们包装成方法。

    • 注: 不论是否显式地定义了一个 getter,类似增量(++)的操作符,都能以预期的方式工作。为了避免产生任何向着不期望的方向的影响,操作符一旦调用 getter ,就会把他的值存在临时变量里。

  1. 抽象方法

    • Instance , getter 和 setter 方法可以是抽象的,也就是定义一个接口,但是把实现交给其他的类。要创建一个抽象方法,使用分号(;)代替方法体:
     abstract class Doer {
        // ...定义实例变量和方法...
        void doSomething(); // 定义一个抽象方法。
     }
    
     class EffectiveDoer extends Doer {
         void doSomething() {
            // ...提供一个实现,所以这里的方法不是抽象的...
         }
     }
    
    
  2. 枚举类型

  assert(Color.red.index == 0);
  assert(Color.green.index == 1);
  assert(Color.blue.index == 2);

```
  List<Color> colors = Color.values;
  assert(colors[2] == Color.blue);   

```

* 你可以在 switch 语句 中使用枚举。如果 e 在 switch (e) 是显式类型的枚举,那么如果你不处理所有的枚举值将会弹出警告:

 ```
  enum Color {
     red,
     green,
     blue
  }
  // ...
  Color 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'
   }
 
 ```

***枚举类型有以下限制***
   * 你不能在子类中混合或实现一个枚举。
   * 你不能显式实例化一个枚举。
  1. 为类添加特征:mixins

    • mixins 是一种多类层次结构的类的代码重用。

    • 要使用 mixins ,在 with 关键字后面跟一个或多个 mixin 的名字。下面的例子显示了两个使用mixins的类:

     class Musician extends Performer with Musical {
          // ...
     }
    
    class Maestro extends Person with Musical, 
        Aggressive, Demented {
    
           Maestro(String maestroName) {
               name = maestroName;
               canConduct = true;
           }
     }
    
    
  1. 类的变量和方法

    • 使用 static 关键字来实现类变量和类方法。

    • 只有当静态变量被使用时才被初始化。

    • 静态变量, 静态变量(类变量)对于类状态和常数是有用的:

        class Color {
           static const red = const Color('red'); // 一个恒定的静态变量
           final String name;      // 一个实例变量。 
           const Color(this.name); // 一个恒定的构造函数。
        }
      
        main() {
           assert(Color.red.name == 'red');
        }
      
      
    • 静态方法, 静态方法(类方法)不在一个实例上进行操作,因而不必访问 this 。例如:

      import 'dart:math';
    
      class Point {
         num x;
         num 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);
         }
      }
    
      main() {
        var a = new Point(2, 2);
        var b = new Point(4, 4);
        var distance = Point.distanceBetween(a, b);
        assert(distance < 2.9 && distance > 2.8);
      }
    
    
    • 注:考虑到使用高阶层的方法而不是静态方法,是为了常用或者广泛使用的工具和功能。

    • 你可以将静态方法作为编译时常量。例如,你可以把静态方法作为一个参数传递给静态构造函数。

抽象类

类-隐式接口

类-扩展一个类

库和可见性

  1. import,part,library指令可以帮助创建一个模块化的,可共享的代码库。库不仅提供了API,还提供隐私单元:以下划线(_)开头的标识符只对内部库可见。每个Dartapp就是一个库,即使它不使用库指令。

  2. 库可以分布式使用包。见 Pub Package and Asset Manager 中有关pub(SDK中的一个包管理器)。

  3. 使用库

  import 'dart:html';

  import 'dart:io';
  import 'package:mylib/mylib.dart';
  import 'package:utils/utils.dart';

  1. 指定库前缀
```
  import 'package:lib1/lib1.dart';
  import 'package:lib2/lib2.dart' as lib2;
  // ...
  var element1 = new Element(); // 使用lib1里的元素
  var element2 =
  new lib2.Element();  // 使用lib2里的元素

```   
  1. 导入部分库

    • 如果想使用的库一部分,你可以选择性导入库。例如:
     // 只导入foo库
     import 'package:lib1/lib1.dart' show foo;
    
     //导入所有除了foo
     import 'package:lib2/lib2.dart' hide foo;
    
    
  2. 延迟加载库

    • 延迟(deferred)加载(也称为延迟(lazy)加载)允许应用程序按需加载库。下面是当你可能会使用延迟加载某些情况:

      • 为了减少应用程序的初始启动时间;
      • 执行A / B测试-尝试的算法的替代实施方式中;
      • 加载很少使用的功能,例如可选的屏幕和对话框。
    • 为了延迟加载一个库,你必须使用 deferred as 先导入它。

       import 'package:deferred/hello.dart' deferred as hello;
       
      
    • 当需要库时,使用该库的调用标识符调用 LoadLibrary()。

       greet() async {
         await hello.loadLibrary();
         hello.printGreeting();
       }
      
      
    • 在前面的代码,在库加载好之前,await关键字都是暂停执行的。有关 async 和 await 见 asynchrony support 的更多信息。

    • 您可以在一个库调用 LoadLibrary() 多次都没有问题。该库也只被加载一次。

    • 当您使用延迟加载,请记住以下内容:

      • 延迟库的常量在其作为导入文件时不是常量。记住,这些常量不存在,直到迟库被加载完成。
      • 你不能在导入文件中使用延迟库常量的类型。相反,考虑将接口类型移到同时由延迟库和导入文件导入的库。
      • Dart隐含调用LoadLibrary()插入到定义deferred as namespace。在调用LoadLibrary()函数返回一个Future。
  1. 库的实现

    • 用 library 来来命名库,用part来指定库中的其他文件。 注意:不必在应用程序中(具有顶级main()函数的文件)使用library,但这样做可以让你在多个文件中执行应用程序。
  1. 声明库

    • 利用library identifier(库标识符)指定当前库的名称:

        // 声明库,名ballgame
        library ballgame;
      
        // 导入html库
        import 'dart:html';
      
        // ...代码从这里开始... 
      
      
  1. 关联文件与库

    • 添加实现文件,把part fileUri放在有库的文件,其中fileURI是实现文件的路径。然后在实现文件中,添加部分标识符(part of identifier),其中标识符是库的名称。下面的示例使用的一部分,在三个文件来实现部分库。

    • 第一个文件,ballgame.dart,声明球赛库,导入其他需要的库,并指定ball.dart和util.dart是此库的部分:

        library ballgame;
      
        import 'dart:html';
        // ...其他导入在这里...
      
        part 'ball.dart';
        part 'util.dart';
      
        // ...代码从这里开始...
      
      
  1. 重新导出库(Re-exporting libraries)
* 可以通过重新导出部分库或者全部库来组合或重新打包库。例如,你可能有实现为一组较小的库集成为一个较大库。或者你可以创建一个库,提供了从另一个库方法的子集。

  ```
   // In french.dart:
   library french;

   hello() => print('Bonjour!');
   goodbye() => print('Au Revoir!');

   // In togo.dart:
   library togo;

   import 'french.dart';
   export 'french.dart' show hello;

   // In another .dart file:
   import 'togo.dart';

   void main() {
       hello();   //print bonjour
       goodbye(); //FAIL
   }
  
  ```

异步的支持

  1. Dart 添加了一些新的语言特性用于支持异步编程。最通常使用的特性是 async 方法和 await 表达式。Dart 库大多方法返回 Future 和 Stream 对象。这些方法是异步的:它们在设置一个可能的耗时操作(比如 I/O 操作)之后返回,而无需等待操作完成

  2. 当你需要使用 Future 来表示一个值时,你有两个选择。

    • 使用 async 和 await
    • 使用 Future API
  3. 同样的,当你需要从 Stream 获取值的时候,你有两个选择。

    • 使用 async 和一个异步的 for 循环 (await for)
    • 使用 Stream API
  4. 使用 async 和 await 的代码是异步的,不过它看起来很像同步的代码。比如这里有一段使用 await 等待一个异步函数结果的代码:

await lookUpVersion()

  1. 要使用 await,代码必须用 await 标记

     checkVersion() async {
        var version = await lookUpVersion();
        if (version == expectedVersion) {
           // Do something.
        } else {
          // Do something else.
          }
     }
    
    
  2. 你可以使用 try, catch, 和 finally 来处理错误并精简使用了 await 的代码。

    
     try {
        server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 4044);
     } catch (e) {
        // React to inability to bind to the port...
     }
    
    
  3. 声明异步函数

  1. 使用带 future 的 await 表达式
 ```
  main() async {
     checkVersion();
     print('In main: version is ${await lookUpVersion()}');
  }
 
 ```
  1. 结合 streams 使用异步循环

    • 一个异步循环具有以下形式:
     await for (variable declaration in expression) {
         // Executes each time the stream emits a value.
     }
    
    
    • 表达式 的值必须有Stream 类型(流类型)。执行过程如下:

      • 在 stream 发出一个值之前等待
      • 执行 for 循环的主体,把变量设置为发出的值。
      • 重复 1 和 2,直到 Stream 关闭
    • 如果要停止监听 stream ,你可以使用 break 或者 return 语句,跳出循环并取消来自 stream 的订阅 。

    • 如果一个异步 for 循环没有正常运行,请确认它是一个异步方法。 比如,在应用的 main() 方法中使用异步的 for 循环时,main() 的方法体必须被 async 标记。

      main() async {
         ...
        
         await for (var request in requestServer) {
             handleRequest(request);
         }
      
         ...
      }
    
    
上一篇下一篇

猜你喜欢

热点阅读