Dart基础语法


重要的概念

  • 任何你可以放在一个变量中的都是一个object,并且每个object都是一个class的实例,甚至于数字,functions,null都是对象,所有的对象继承自Object类。
  • 即使Dart是强类型的,但是类型说明是可选的,因为Dart可以推断类型;当你要显式说明没有指定任何类型的时候,可以用dynamic
  • Dart也支持泛型,比如List(整数列表)或者List(一个任意类型的对象列表)
  • Dart支持顶层函数(如main()),以及绑定到一个类或者对象的方法(静态方法或者实例方法);
  • 同样的,Dart支持顶层变量;
  • 不像Java,Dart中没有public,private,protected这些关键字,如果一个标识符以“_”(下划线)符号开头,则对其库来说是私有的。
  • 标识符可以以字母或者下划线开头,后加字母或者数字组合
  • Dart有表达式(有运行时值)和语句(没有运行时值), 如condition ? exp1:exp2,与if else 语句相比,前则有值,后者没有值。一个语句经常包含一个或者多个表达式,但是一个表达式不能直接包含一个语句。
  • Dart工具可以报告两种问题:errors和warnings。

变量

var name = 'Bob';

dynamic name = 'Bob';//后面可以改变类型

String name = 'Bob';

默认值

没有初始化的变量有一个初始值null,即使是数字类型也一样,因为数字类型也是一个对象。

int lineCount;
assert(lineCount == null);

final 和 const

如果你有一个变量不会改变,应该使用final或者const。
一个const变量是一个编译时常量,而一个final 顶层或者类变量当第一次使用的时候初始化。

注意:实例变量可以是final,但不是const。final型实例变量必须在构造函数体执行之前(变量声明的时候)就初始化。

在你需要一个编译时常量的时候可以使用const,如果const常量在Class层级,则用static const,在声明的地方将变量设置为编译时常量,比如一个数字,一个字符串,一个const常量,或者一系列常量运算 的结果。

final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';

const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere

Const关键字不仅可以用于声明常量,还可以用它创建常量值,以及声明创建常量值的构造器,任何变量可以有一个常量。

var foo = const [];  //你后面可以修改foo,如foo = [1, 2, 3]; 
final bar = const [];
const baz = []; // Equivalent to `const []`,后面不能修改baz

内建类型

  • numbers

有两种形式,int(64位,依赖于平台,在Dart VM中,为64位)和double(64位)

  • strings

一个Dart String是一系列UTF-16代码单元,可以使用单引号和双引号。
可以使用${expression}在字符串中表示动态内容,大括号可以省略,对于对象,会使用其toString()方法;

可以用三个引号创建多行文本:

   var s1 = '''
   You can create
   multi-line strings like this one.
   ''';

   var s2 = """This is also a
   multi-line string.""";

可以用一个r创建一个raw string,比如Unicode字符串

   var s = r'In a raw string, not even \n gets special treatment.';
  • booleans

为了表示布尔值,Dart有一个叫做bool的类型。

  • list/arrays

在Dart中,arrays是List对象,这里就直接叫做lists了。

    var list = [1, 2, 3];
    assert(list.length == 3);
  assert(list[1] == 2);

  list[1] = 1;
  assert(list[1] == 1);

为了创建一个编译时常量的list,在list 字面量前加const,如:

  var constantList = const [1, 2, 3];
  • maps

键值对,不用多说,上代码:

      var gifts = {
        // Key:    Value
    'first': 'partridge',
        'second': 'turtledoves',
        'fifth': 'golden rings'
          };

      var nobleGases = {
        2: 'helium',
        10: 'neon',
        18: 'argon',
          };


      var gifts = Map();
          gifts['first'] = 'partridge';
          gifts['second'] = 'turtledoves';
          gifts['fifth'] = 'golden rings';

          var nobleGases = Map();
          nobleGases[2] = 'helium';
          nobleGases[10] = 'neon';
          nobleGases[18] = 'argon';

          //添加一个新的键值对
          var gifts = {'first': 'partridge'};
          gifts['fourth'] = 'calling birds'; // Add a key-value pair

          //取值
          var gifts = {'first': 'partridge'};
          assert(gifts['first'] == 'partridge');

          //如果不存在,会返回null
          var gifts = {'first': 'partridge'};
          assert(gifts['fifth'] == null);

          //编译时常量
          final constantMap = const {
        2: 'helium',
            10: 'neon',
            18: 'argon',
              };

              // constantMap[2] = 'Helium'; // Uncommenting this causes an error.
  • runes(用于在字符串中标识Unicode字符)

Dart中, runes是字符串的UTF-32代码点。
由于Dart使用的是UTF-16,因此在字符串中表示32位字符,需要特殊的语法。
一般表示方式是\uXXXX,XXXX是一个4位16进制数


    main() {
    var clapping = '\u{1f44f}';
    print(clapping);
    print(clapping.codeUnits);
    print(clapping.runes.toList());

  Runes input = new Runes(
    '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
     print(new String.fromCharCodes(input));
    }
  • symbols

一个Symbol对象表示一个Dart程序中声明的运算符或者标识符。你可能永远都不会使用这个。不多说。

Function

Dart是一个真正面向对象的语言,即使是function都有一个叫做Function的对象。也就是说,Function可以赋值给一个变量,或者作为参数传递给其他functions,你也可以像调用函数一样调用Dart实例,如Callable Classes。(后面又说,暂时忽略即可)

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

//如果函数只是包含了一个简单的表达式,可以用速记方法表示
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

=> expr 是 { return expr; }的速写方式。值得注意的是,这里只可以用表达式的速写,而不可以用语句的速写,比如不可以使用if语句。

一个function有两种类型的参数,required和optional,必须的参数放在最前面,可选参数放在后面。命名的可选参数也可以用@required标记。

可选参数可以是位置性的,也可以是命名的,但是不能包含这两种情况。

可选命名参数

//定义
void enableFlags({bool bold, bool hidden}) {...}

enableFlags(bold: true, hidden: false);

Flutter中创建实例的参数很多,因此主要使用可选命名参数这种方式,便于阅读。

你也可以用@required注解表示这是个必须的参数,如:

const Scrollbar({Key key,  Widget child})

Required 是在meta 包下面定义的,因此你需要import package:meta/meta.dart 或者,import那些输出meta包的包,如Flutter下的package:flutter/material.dart.

可选位置参数

用[] 包裹着的参数:

String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

assert(say('Bob', 'Howdy') == 'Bob says Howdy');

assert(say('Bob', 'Howdy', 'smoke signal') ==
    'Bob says Howdy with a smoke signal');

默认参数值

你的function可以用=为可选位置参数或者可选命名参数定义默认值,而且必须使用编译时常量值,如果没有提供默认值,那么默认值为null。

/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold = false, bool hidden = false}) {...}

// bold will be true; hidden will be false.
enableFlags(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;
}

assert(say('Bob', 'Howdy') ==
    'Bob says Howdy with a carrier pigeon');

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');
}    

main()

每一个APP都必须有一个main()方法,作为app的入口点。main()方法返回void,可以有一个List 作为参数。

void main() {
  querySelector('#sample_text_id')
    ..text = 'Click me!'
    ..onClick.listen(reverseText);
}

这种..的语法叫做级联。通过级联,你可以对单个对象的成员执行多个操作。

// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
  print(arguments);

  assert(arguments.length == 2);
  assert(int.parse(arguments[0]) == 1);
  assert(arguments[1] == 'test');
}

你也可以用args库,来定义和解析命令行参数。

作为第一类对象的functions

你可以将一个function作为参数传递给另一个function:

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

var list = [1, 2, 3];

// Pass printElement as a parameter.
list.forEach(printElement);

也可以将一个function赋值给一个变量:

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

匿名函数

你可以创建一个没有命名的函数,如匿名函数,lambda,闭包等。
下面的代码块就包含了一个函数体:

([[Type] param1[,]]) { 
  codeBlock; 
}; 

如下面这个方法,使用了没有指定类型的参数:

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

如果函数只包含一个语句,可以用=> 箭头速写方式。

list.forEach(
    (item) => print('${list.indexOf(item)}: $item'));

嵌套函数变量作用域问题

bool topLevel = true;

void main() {
  var insideMain = true;

  void myFunction() {
    var insideFunction = true;

    void nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

nestedFunction()可以使用每一个层级的变量。

闭包

参考JavaScript中的闭包

/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

void main() {
  // Create a function that adds 2.
  var add2 = makeAdder(2);

  // Create a function that adds 4.
  var add4 = makeAdder(4);

  assert(add2(3) == 5);
  assert(add4(3) == 7);
}

返回值

所有function都会返回一个值,如果没有指定,则返回null:

foo() {}

assert(foo() == null);

操作符

基本的就不说了,和其他语言的大同小异,如Java,JavaScript等。

不同的是:

?? 代表 if null
?. 表示 如果不为null才执行(参考kotlin)
~/ 代表 整除(返回值为int)
/ 返回值 为double
as 用来类型转换(和kotlin一样)
is 判断是否属于否个类型

..(级联操作符)

级联操作符允许你在相同的对象上执行一系列操作。除了函数调用,你还可以通过级联访问同一个对象上的多个字段(field)。

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();

在返回实际对象的function上运用级联要非常小心,

var sb = StringBuffer();
sb.write('foo')
  ..write('bar'); // Error: method 'write' isn't defined for 'void'.

Sb.write()返回void,你不能在void上构造级联。

控制流/异常

和其他语言大同小异,不多说。

Class

创建对象

var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});

也可以通过常量构造器创建编译时常量

var p = const ImmutablePoint(2, 2);

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // They are the same instance!

子类不从父类继承构造函数,如果子类没有声明构造函数,则是默认的午餐构造函数。

获取一个对象的类型

可以使用runtimeType属性获取一个对象的类型,这个属性返回一个Type类型的对象。

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

命名构造函数

class Point {
  num x, y;

  Point(this.x, this.y);

  // Named constructor
  Point.origin() {
    x = 0;
    y = 0;
  }
}

调用一个非默认的超类构造函数

默认情况下,一个子类的构造函数调用超类的没有命名的无参构造函数。超类的构造函数在构造函数体的开始被调用,如果还是用了初始化列表,则初始化列表先于超类被调用。
执行顺序如下:

  1. 初始化列表
  2. 超类的无参构造函数
  3. main class的无参构造函数

如果超类没有无命名的无参构造函数,你必须手动调用它的一个构造函数:

class Employee extends Person {
  Employee() : super.fromJson(getDefaultData());
  // ···
}

初始化列表

除了调用超类构造函数之外,还可以在构造函数体执行之前初始化实例变量。

// 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)');
}

初始化程序的右边部分无法访问this。

开发过程中也可以使用assert进行输入验证:

Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}

重定向构造函数

class 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);
}

常量构造函数

如果你的类提供用不改变的对象,那么你可以将那些对象设置为编译时常量。为了实现这种效果,你需要定义个常量构造函数,并且确认所有的实例变量都是final类型的。

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}

常量构造函数也不总是创建常量。

工厂构造函数

借助factory关键字,你不用每次创建对象的时候都创建一个新的对象,而可以从缓存中返回,或者返回一个子类型的对象。

下面是一个从缓存中返回对象的例子:

class 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);
  }
}

值得注意的是工厂构造函数无法访问this。

调用工厂构造方法和调用其他构造方法的方式是一样的:

var logger = Logger('UI');
logger.log('Button clicked');

Method/方法

Getter / Setter

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // Define two calculated properties: 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;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

抽象方法

抽象方法仅存在于抽象类中;

abstract class Doer {
  // Define instance variables and methods...

  void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // Provide an implementation, so the method is not abstract here...
  }
}

抽象类

抽象类是不可以实例化的。如果你想要你的抽象类看起来是实例化的,可以定义一个工厂构造函数。

抽象类通常都有抽象方法。

// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
  // Define constructors, fields, methods...

  void updateChildren(); // Abstract method.
}

隐式接口

每个类都隐式定义一个接口,该接口包含该类的所有实例成员,以及其实现的任何接口;如果你想要在不继承B类实现的情况下创建支持B类API的A类,则A类应该实现B接口。

类可以通过 implements 实现一个或者多个接口:

// A person. The implicit interface contains greet().
class Person {
  // In the interface, but visible only in this library.
  final _name;

  // Not in the interface, since this is a constructor.
  Person(this._name);

  // In the interface.
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// An implementation of the Person interface.
class Impostor implements Person {
  get _name => '';

  String greet(String who) => 'Hi $who. Do you know who I am?';
}

String greetBob(Person person) => person.greet('Bob');

void main() {
  print(greetBob(Person('Kathy')));
  print(greetBob(Impostor()));
}

实现多个接口:

class Point implements Comparable, Location {...}

拓展一个类

使用extends创建子类,使用super引用超类。

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}

class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}
重写成员

子类可以重写实例方法,Getter和Setter,你可以用@override表示你打算重写某个承运。

class SmartTelevision extends Television {
  
  void turnOn() {...}
  // ···
}
重写操作符

你可以重写操作符,比如你创建了一个Vector类,可以重写+操作符进行向量的加法:

!=不可以重写,因为这只是一个语法糖。

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  // Operator == and hashCode not shown. For details, see note below.
  // ···
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}

如果你重写 ==那么你也得重写hashCode

noSuchMethod()

为了监测和响应调用没有实现的方法或者实例变量的情况,你应该重写noSuchMethod()方法;

class A {
  // Unless you override noSuchMethod, using a
  // non-existent member results in a NoSuchMethodError.
  
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}

枚举类型

enum Color { red, green, blue }

每个枚举类型中的值都有一个index getter,

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流程中:

var 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'
}

向class中添加特征:mixins

Mixins是一种在多个类层次中重用类代码的方式。

可以用with 关键字使用mixins:

class Musician extends Performer with Musical {
  // ···
}

class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}

为了实现一个mixin,创建一个继承Object的类,不要声明构造函数,不要调用super,如:

abstract class Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;

  void entertainMe() {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self');
    }
  }
}

Class 变量和方法

和Java中的静态方法一样,用static表示:

class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

静态变量使用的时候才会初始化。

静态方法:

import 'dart:math';

class Point {
  num x, 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);
  }
}

void main() {
  var a = Point(2, 2);
  var b = Point(4, 4);
  var distance = Point.distanceBetween(a, b);
  assert(2.8 < distance && distance < 2.9);
  print(distance);
}

考虑用顶层函数替代静态方法。

泛型

和Java一样,也都是List形式的。

使用集合字面量

List和map字面量都可以泛化:

var names = <String>['Seth', 'Kathy', 'Lars'];
var pages = <String, String>{
  'index.html': 'Homepage',
  'robots.txt': 'Hints for web robots',
  'humans.txt': 'We are people, not machines'
};

在构造函数中使用泛型

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = Set<String>.from(names);

var views = Map<int, View>();

泛型集合和它们包含的类型

与Java不同,Dart中的泛型在运行时是可以携带类型信息的。

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true

限定泛型

class Foo<T extends SomeBaseClass> {
  // Implementation goes here...
  String toString() => "Instance of 'Foo<$T>'";
}

class Extender extends SomeBaseClass {...}

//使用子类作为泛型参数
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();

//不指定泛型也可以
var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'

泛型方法

T 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;
}

Librarys 和它的可见性

可见性: _ 表示仅仅对该Library可见。

每一个Dart App都是一个Library。

import 'dart:html';

import 'package:test/test.dart';

import '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();


// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;

库的懒加载

懒加载(延迟加载)允许app按需加载一个库。

下面是用到懒加载的一些场景:

  • 为了减少一个app的初始化启动时间
  • 进行A/B测试
  • 加载很少被使用的库

使用方式:必须首先用deferred as

import 'package:greetings/hello.dart' deferred as hello;

要使用的时候,调用库的loadLibrary()方法:

Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

以上代码中,await表示等待hello库加载完成才继续执行。

你可以多次调用一个库的loadLibrary()方法,但是它只会被加载一次。

当你使用库的懒加载的时候,需要注意下面这些:

  • 一个懒加载库中的常量不是导入文件中的常量,在库加载之前,这些常量都不存在;
  • 你不能在导入文件中使用懒加载库中的类型,但是,你可以考虑将接口类型移动到由懒加载库和导入文件导入的库。
  • Dart隐式的将loadLibrary()插入到使用deferred as namespace 定义的命名空间中,loadLibray()返回Future类型。

注意:即使是在loadLibrary()执行之前,Dart VM也允许访问懒加载库中的成员,这个特征在后面可能会变更,应该避免使用。

异步支持

Dart中很多函数都返回Future和Stream对象。 这些函数都是异步的。

async/await 支持你在Dart中进行异步编程。

当你使用await的时候,必须在方法外面用async包裹。

Future checkVersion() async {
  var version = await lookUpVersion();
  // Do something with version
}

必要的时候需要处理异常:

try {
  version = await lookUpVersion();
} catch (e) {
  // React to inability to look up the version
}

在异步方法中,你可以多次使用await

var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);

声明异步函数

Future<String> lookUpVersion() async => '1.0.0';

如果不返回一个有用的值,可以用Future<void>

处理Streams

可以从Stream中获取值。

你需要用async和一个异步循环(await for)

注意:在使用await for之前,确信你知道要一直等待循环获取到所有结果。比如在处理UI事件的时候,就不应该使用这个,因为UI事件是无尽的。

await for (varOrType identifier in expression) {
  // Executes each time the stream emits a value.
}
Future main() async {
  // ...
  await for (var request in requestServer) {
    handleRequest(request);
  }
  // ...
}

你也可以用break或者return终止循环。

生成器

当你需要laily生成一系列值,你可以使用生成器。

目前有两种生成器:

  • 同步生成器: 返回Iterable对象
  • 异步生成器: 返回Stream对象

同步生成器: 用 sync* 标记方法,用yield传递值:

Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

异步生成器: 用async* 标记方法,用yield传递值:

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

如果你的生成器是递归的,可以用yield*提升性能:

Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

参考文档


文章作者: 姜康
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 姜康 !
评论
 上一篇
Flutter时间处理 Flutter时间处理
Flutter 中在时间处理方面还是比较简单的,我们主要用到的有两个类: DateTime 表示一个指定的时间点,包括年月日时分秒 TimeOfDay 表示一天中的一个时间点,可以区分上午,下午,12小时制,24小时制等 获取当前时
2020-05-08
下一篇 
Flutter-实现图片圆角的几种方式 Flutter-实现图片圆角的几种方式
关注 https://github.com/jiangkang/flutter-system 了解更多Flutter相关的知识 图片圆角在开发中还是非常常见的,用Flutter实现圆角还是非常简单的,目前了解的大概有下面这几种: 通过
2020-05-08
  目录