Dart是一种面向对象的语言 包含类和基于 mixin 的继承两部分。每一个对象是一个类的实例, 而且 Object.是全部类的父类。 基于 mixin 的继承指的是每一个类(除了 Object )都只有一个父类,类体还能够在多个类继承中被重用。html
要建立一个对象,你可使用 new
关键词并在其后跟上一个构造函数 .构造函数能够写成 ClassName
或者ClassName.identifier
. 好比:java
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);
对象拥有由函数和数据组成的成员(方法 和 实例变量).当你调用一个方法时, 是基于一个对象调用它的: 这个方法可以访问该对象的全部方法和数据.git
使用一个点(.
) 引用实例变量或方法:程序员
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));
使用 ?.
代替 .
来避免当最左操做数为null时产生的异常:github
// If p is non-null, set its y value to 4. p?.y = 4;
一些类提供常量构造函数。 要使用常量构造函数建立编译时常数,请使用const
代替 new
:web
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!
在运行时获取一个对象的类型, 你可使用Object类的 runtimeType
属性, 该属性返回一个 Type 对象.express
print('The type of a is ${a.runtimeType}');
如下部分讨论如何实现类.编程
声明实例变量:json
class Point { num x; // Declare instance variable x, initially null. num y; // Declare y, initially null. num z = 0; // Declare z, initially 0. }
全部为初始化的实例变量值为 null
.
全部实例变量都生成一个隐式的 getter 方法. 非最终实例变量也生成一个隐式 setter 方法. 更多查看 Getters 和 setters.
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. }
若是你要在实例变量声明的时候为其初始化值(而不是经过构造函数或方法),那么当建立实例时就该为其设值,该值在构造函数和其初始化程序列表以前执行.
经过建立一个与其类名相同的函数来声明构造函数 (plus, optionally, an additional identifier as described inNamed 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; } }
this
关键字引用当前实例.
Note:只有当命名出现冲突时使用
this
. 不然,Dart风格指南建议不要用this
.
向实例变量分配构造函数的参数是很常见的一种模式,Dart语法糖让其变得容易:
class Point { num x; num y; // Syntactic sugar for setting x and y // before the constructor body runs. Point(this.x, this.y); }
默认构造函数
若是您没有声明构造函数,则会为您提供默认构造函数。 默认构造函数没有参数,并调用父类中的无参数构造函数。.
构造函数不能继承
子类不会从他们的超类继承构造函数.声明没有构造函数的子类只有默认(无参数,无名称)构造函数.
命名构造函数
使用命名构造函数为类实现多个构造函数或提供额外的声明:
class Point { num x; num y; Point(this.x, this.y); // Named constructor Point.fromJson(Map json) { x = json['x']; y = json['y']; } }
请记住,构造函数不是继承的,这意味着父类的命名构造函数不会被子类继承。 若是要使用父类中定义的命名构造函数建立子类,则必须在子类中实现该构造函数.
调用父类的非默认构造函数
默认状况下,子类中的构造函数调用超类的未命名的无参数构造函数。 超类的构造函数在构造函数体的起始处被调用。 若是一个 初始化器列表 也被使用,它将在超类被调用以前执行。 总而言之,执行顺序以下:
若是超类没有未命名的无参数构造函数,则必须手动调用超类中的一个构造函数。 在冒号 (:
)以后,在构造函数体(若是有的话)以前指定超类构造函数.
下面的例子中,Employee类的构造函数调用了其父类Person的命名构造函数. 点击运行按钮 ( ) 执行代码.
class 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'); } } main() { var emp = new Employee.fromJson({}); // Prints: // in Person // in Employee if (emp is Person) { // Type check emp.firstName = 'Bob'; } (emp as Person).firstName = 'Bob'; }
由于在调用构造函数以前对父类构造函数的参数进行求值,因此参数能够是一个表达式,例如函数调用:
class Employee extends Person { // ... Employee() : super.fromJson(findDefaultData()); }
Note: 在构造函数初始化列表中使用
super()
时,将其放在最后. 更多信息查看 Dart usage guide.
Warning: 父类的构造函数的参数无权访问
this
. 好比, 参数能访问静态方法不能访问实例方法.
Initializer list
除了调用超类构造函数以外,还能够在构造函数体运行以前初始化实例变量,用逗号分隔初始化器.
class Point { num x; num y; Point(this.x, this.y); // Initializer list sets instance variables before // the constructor body runs. Point.fromJson(Map jsonMap) : x = jsonMap['x'], y = jsonMap['y'] { print('In Point.fromJson(): ($x, $y)'); } }
Warning: 初始化程序的右侧没法访问
this
.
初始化器列表在设置final字段时很方便。 如下示例在初始化程序列表中初始化三个final字段。 单击运行按钮( ) 执行代码.
import 'dart:math'; class Point { final num x; final num y; final num distanceFromOrigin; Point(x, y) : x = x, y = y, distanceFromOrigin = sqrt(x * x + y * y); } main() { var p = new Point(2, 3); print(p.distanceFromOrigin); }
重定向构造函数
有时,构造函数的惟一目的是重定向到同一个类中的另外一个构造函数。 重定向构造函数的正文是空的,构造函数调用出如今冒号(:)以后.
class Point { num x; num y; // The main constructor for this class. Point(this.x, this.y); // Delegates to the main constructor. Point.alongXAxis(num x) : this(x, 0); }
常量构造函数
若是您的类生成永远不会更改的对象,则可使这些对象的编译时常量。 为此,定义一个 const
构造函数,并确保全部实例变量都是 final
.
class ImmutablePoint { final num x; final num y; const ImmutablePoint(this.x, this.y); static final ImmutablePoint origin = const ImmutablePoint(0, 0); }
Factory 构造函数
当不须要老是建立该类的新实例时,使用 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 = new Logger._internal(name); _cache[name] = logger; return logger; } } Logger._internal(this.name); void log(String msg) { if (!mute) { print(msg); } } }
Note: 工厂构造函数不能访问
this
.
调用工厂构造函数, 使用 new
关键字:
var logger = new Logger('UI'); logger.log('Button clicked');
方法是为对象提供行为的函数.
实例方法
对象上的实例方法能够访问实例变量和 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); } }
Getters and setters
Getters和setter是为对象的属性提供读写访问权限的特殊方法。 回想一下,每一个实例变量都有一个隐式的getter,若是合适的话加一个setter。 您可使用 get
和 set
关键字来实现getter和setter来建立其余属性
class Rectangle { num left; num top; num width; num 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; } main() { var rect = new Rectangle(3, 4, 20, 15); assert(rect.left == 3); rect.right = 12; assert(rect.left == -8); }
使用getter和setter,您能够从实例变量开始,稍后用方法包装它们,而不用改变客户端代码.
Note: 如运算符(++)以预期的方式工做,不管是否明肯定义了getter. 为了不任何意外的发生,操做符只调用一次getter,将其值保存在临时变量中.
抽象方法
实例,getter和setter方法能够是抽象的,定义一个接口,但将其实现交给其余类。 要使方法变成抽象方法,请使用分号 (;) 而不是方法体:
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... } }
调用抽象方法会致使运行时错误。.
查看 Abstract classes.
可覆盖的操做符
您能够覆盖下表中显示的运算符。 例如,若是定义Vector(向量)类,则能够定义一个+
方法来添加两个向量.
下面是覆盖 +
和 -
操做符的实例:
class Vector { final int x; final int y; const Vector(this.x, this.y); /// Overrides + (a + b). Vector operator +(Vector v) { return new Vector(x + v.x, y + v.y); } /// Overrides - (a - b). Vector operator -(Vector v) { return new Vector(x - v.x, y - v.y); } } main() { final v = new Vector(2, 3); final w = new Vector(2, 2); // v == (2, 3) assert(v.x == 2 && v.y == 3); // v + w == (4, 5) assert((v + w).x == 4 && (v + w).y == 5); // v - w == (0, 1) assert((v - w).x == 0 && (v - w).y == 1); }
若是你覆盖 ==
, 你也应该覆盖Object的 hashCode
的 getter方法. 覆盖 ==
和 hashCode
的例子, 查看 Implementing map keys.
有关覆盖的更多信息, 参阅 Extending a class.
使用 abstract
修饰符来定义一个 抽象类— 一个不能实例化的类. 抽象类对于定义接口颇有用,一般有一些实现. 若是想让抽象类变得可实例化 请使用 factory constructor .
抽象类一般具备 抽象方法. 这是一个声明一个抽象类的例子,它有一个抽象方法:
// This class is declared abstract and thus // can't be instantiated. abstract class AbstractContainer { // ...Define constructors, fields, methods... void updateChildren(); // Abstract method. }
如下类不是抽象的,所以即便它定义了一个抽象方法也能够实例化:
class SpecializedContainer extends AbstractContainer { // ...Define more constructors, fields, methods... void updateChildren() { // ...Implement updateChildren()... } // Abstract method causes a warning but // doesn't prevent instantiation. void doSomething(); }
每一个类隐式定义一个包含类的全部实例成员以及它实现的任何接口的接口。 若是要建立一个支持B类API而不继承B实现的A类,则A类应实现B接口.
一个类实现一个或多个接口经过在 implements
子句中声明它们,而后提供接口所需的API来实现。 例如:
// 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(who) => 'Hello, $who. I am $_name.'; } // An implementation of the Person interface. class Imposter implements Person { // We have to define this, but we don't use it. final _name = ""; String greet(who) => 'Hi $who. Do you know who I am?'; } greetBob(Person person) => person.greet('bob'); main() { print(greetBob(new Person('kathy'))); print(greetBob(new Imposter())); }
下面是一个指定一个类实现多个接口的例子:
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 { @override void turnOn() { // ... } // ... }
有时缩小方法参数的类型或保证代码中实例变量是 type safe, 可使用 covariant
关键字.
noSuchMethod()
为了检测或应对尝试使用不存在的方法或实例变量, 你能够复写 noSuchMethod()
:
class A { // Unless you override noSuchMethod, using a // non-existent member results in a NoSuchMethodError. void noSuchMethod(Invocation mirror) { print('You tried to use a non-existent member:' + '${mirror.memberName}'); } }
若是你使用 noSuchMethod()
为一个或多个类型实现可能的getter、setter、方法, 你可使用 @proxy
注解来避免警告:
@proxy class A { void noSuchMethod(Invocation mirror) { // ... } }
一种替代 @proxy
的方式是, 若是在编译时你知道有那些类型, 就是类声明实现了的类.
class A implements SomeClass, SomeOtherClass { void noSuchMethod(Invocation mirror) { // ... } }
了解更多注解信息, 查看 Metadata.
枚举类型, 一般被称为 enumerations 或 enums, 是用于表示固定数量的常量值的特殊类.
使用枚举
使用 enum
关键字声明一个枚举类型:
enum Color { red, green, blue }
枚举中的每一个值都有一个 index
getter, 它返回枚举声明中的值从零的位置开始。 例如,第一个值具备索引0,第二个值具备索引1.
assert(Color.red.index == 0); assert(Color.green.index == 1); assert(Color.blue.index == 2);
要获取枚举中全部值的列表,使用枚举的 values
常量.
List<Color> colors = Color.values; assert(colors[2] == Color.blue);
能够在 switch 语句中使用枚举. 若是switch (e)
中的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' }
枚举类型具备如下限制:
更多信息查看Dart语言规范.
Mixins是在多个类层次结构中重用类的代码的一种方式.
要使用mixin,请使用with
关键字后跟一个或多个mixin名称。 如下示例显示使用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'); } } }
Note: 从1.13起,Dart VM已经取消了对mixin的两个限制::
- Mixins容许从Object之外的类扩展.
- Mixins能够调用
super()
.dart2js 中还不支持这些 “super mixins” , 而且在Dart分析器中须要
--supermixin
标志.
更多信息查看 文章 Mixins in Dart.
使用 static
关键字来实现类范围的变量和方法.
静态变量
静态变量(类变量)对于类范围的状态和常量颇有用:
class Color { static const red = const Color('red'); // A constant static variable. final String name; // An instance variable. const Color(this.name); // A constant constructor. } main() { assert(Color.red.name == 'red'); }
直到使用时静态变量才被初始化.
Note: 此页面遵循 风格指南建议 ,优选使用
lowerCamelCase
做为常量名称.
静态方法
静态方法(类方法)不对一个实例进行操做,所以没法访问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); }
Note: 考虑使用顶级函数,而不是静态方法,用于经常使用或普遍使用的实用程序和功能.
您可使用静态方法做为编译时常量。 例如,您能够将静态方法做为参数传递给常量构造函数.
若是您查看基本数组类型List的API文档, List, 您将看到类型其实是List<E>
. <…> 符号将List标记为 通用 (或参数化)类型—一个有正规类型参数的类型. 按照惯例,类型变量具备单字母名称,例如: E, T, S, K, 和 V.
由于Dart中的类型是可选的,因此您没必要使用泛型 . 可是,您可能但愿,因为一样的缘由,您可能但愿在代码中使用其余类型:types(通用或不通用)可以让您记录和注释代码,从而使您的意图更清晰.
例如,若是您打算列出只包含字符串,则能够将其声明为 List<String>
(将其做为“List of string). 这样你,你的同行程序员和你的工具(如IDE和Dart VM在检查模式下)能够检测到将非字符串分配给列表多是一个错误。 如下是一个例子:
var names = new List<String>(); names.addAll(['Seth', 'Kathy', 'Lars']); // ... names.add(42); // Fails in checked mode (succeeds in production mode).
使用泛型的另外一个缘由是减小代码重复.泛型让您在多个类型之间共享一个接口和实现, 同时仍然利用检查模式和静态分析预警。 例如,假设您建立一个用于缓存对象的接口:
abstract class ObjectCache { Object getByKey(String key); setByKey(String key, Object value); }
你发现你想要这个接口的字符串特定版本,因此你建立另外一个接口:
abstract class StringCache { String getByKey(String key); setByKey(String key, String value); }
接着你想要这个接口的数字特别版本… 因而你有了个主意.
通用类型能够节省您建立全部这些接口的麻烦。 相反,您能够建立一个接受类型参数的单一接口:
abstract class Cache<T> { T getByKey(String key); setByKey(String key, T value); }
在此代码中,T是占位类型。 这是一个占位符,您能够将其视为开发人员稍后将定义的类型.
List 和 map 字面量能被参数化.参数化字面量就像你以前见过的字面量同样,除了你在括号以前使用的 <type>
(对于list集合) 或 <keyType, valueType>
(对于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 = new List<String>(); names.addAll(['Seth', 'Kathy', 'Lars']); var nameSet = new Set<String>.from(names);
下例中建立了一个键为Integer类型,值为View类型的map集合:
var views = new Map<int, View>();
Dart泛型类型被 修改, 意味着会附带类型信息. 例如,您能够测试集合的类型,即便在生产模式下:
var names = new List<String>(); names.addAll(['Seth', 'Kathy', 'Lars']); print(names is List<String>); // true
可是,is
表达式仅检查集合的类型 —而不是里面的对象. 在生产模式下, List<String>
里面可能含有非String类型的项. 解决方案是检查每一项的类型或使用异常处理程序包裹项操做代码 (查看 Exceptions).
Note: 相比之下,Java中的泛型使用擦除,这意味着泛型类型参数在运行时被删除。 在Java中,您能够测试对象是否为List,可是不能测试它是否为
List<String>
.
实现泛型类型时,可能须要限制其参数的类型。 你可使用extends
来实现 .
// T must be SomeBaseClass or one of its descendants. class Foo<T extends SomeBaseClass> {...} class Extender extends SomeBaseClass {...} void main() { // It's OK to use SomeBaseClass or any of its subclasses inside <>. var someBaseClassFoo = new Foo<SomeBaseClass>(); var extenderFoo = new Foo<Extender>(); // It's also OK to use no <> at all. var foo = new Foo(); // Specifying any non-SomeBaseClass type results in a warning and, in // checked mode, a runtime error. // var objectFoo = new Foo<Object>(); }
最初,Dart的通用支持仅限于类。 一种较新的语法(称为泛型方法)容许在方法和函数上使用类型参数:
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; }
在 first
(<T>
) 中的泛型类型参数 容许在多个地方使用参数T
:
T
).List<T>
).T tmp
).版本要点: SDK 1.21. 中介绍了泛型方法的新语法。 若是使用泛型方法,请选用 SDK版本为1.21或更高版本.
关于泛型的更多信息, 参阅 Dart中的可选类型 和 使用通用方法.
import
和 library
指令能让你建立模块化和可共享的代码库. 库不只提供API,并且是一个隐私单位: 如下划线(_)开头的标识符只能在库内部显示。 每一个Dart应用程序都是一个库, 即便它不使用library
指令.
库可使用包分发. 参阅 Pub Package and Asset Manager 查看关于 pub 组件的更多信息, 软件包管理器包含在SDK中.
使用 import
来指定一个库的命名空间在另一个库的做用域内被使用.
例如, Dart Web应用程序一般使用 dart:html 库, 能够这样引入:
import 'dart:html';
import
惟一须要的参数是指定库的URI. 对于内置库,URI具备特殊的dart:scheme。 dart:
scheme. 对于其余库,您可使用文件系统路径或 package:
scheme. package:
scheme 指定由程序包管理器(如pub工具)提供的库. 例如:
import 'dart:io'; import 'package:mylib/mylib.dart'; import 'package:utils/utils.dart';
Note: URI 表明统一的资源标识符. URLs(统一资源定位符)是一种常见的URI.
指定库前缀
若是导入两个库的标识符具备冲突,那么您能够为一个或两个库指定前缀。 例如,若是library1和library2都有一个Element类,那么你可能会有这样的代码:
import 'package:lib1/lib1.dart'; import 'package:lib2/lib2.dart' as lib2; // ... Element element1 = new Element(); // Uses Element from lib1. lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.
引入库的一部分
若是您只想使用库的一部分,则能够有选择地导入库. 例如:
// 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:deferred/hello.dart' deferred as hello;
当须要一个库时, 使用库的标识符调用 loadLibrary()
.
greet() async { await hello.loadLibrary(); hello.printGreeting(); }
在上述代码中,直到库被加载 await
关键字将暂停执行. 查看更多关于 async
和 await
的信息, 参阅 asynchrony support.
您能够在库中屡次调用loadLibrary()
,而不会出现问题。 该库仅加载一次.
使用延期加载时,请记住如下几点:
loadLibrary()
插入到使用 deferred as namespace
定义的命名空间中。loadLibrary()
函数返回一个 Future.查看 建立 Library Packages 关于如何实现库包的建议.
Dart具备几种支持异步编程的语言特性。 这些功能最经常使用的是 async
函数和 await
表达式.
Dart库充满了返回Future或Stream对象的函数。 这些功能是异步的: 在设置可能耗时的操做(例如I / O)后返回,而不等待该操做完成.
当您须要使用由Future表明的值时,您有两个选择:
async
和 await
一样,当您须要从Stream获取值时,您有两个选项:
async
和一个 asynchronous for loop (await for
)使用async
和 await
的代码是异步的,但它看起来很像同步代码。 例如,这里有一些使用await
等待异步功能的结果的代码:
await lookUpVersion()
要使用 await
,代码必须在标记为async
的函数中:
checkVersion() async { var version = await lookUpVersion(); if (version == expectedVersion) { // Do something. } else { // Do something else. } }
你可使用 try
, catch
, 和 finally
处理和清理 await
代码中的错误:
try { server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 4044); } catch (e) { // React to inability to bind to the port... }
一个 async function是一个函数,其主体标有 async
标记. 尽管异步功能可能会执行耗时的操做, 可是在任何正文执行以前,它将当即返回.
checkVersion() async { // ... } lookUpVersion() async => /* ... */;
将async
关键字添加到函数上使其返回Future. 例如,考虑这个同步函数,它返回一个String:
String lookUpVersionSync() => '1.0.0';
若是将其更改成异步函数—例如,因为未来的实现将会耗费时间—返回的值是Future:
Future<String> lookUpVersion() async => '1.0.0';
请注意,该函数的主体不须要使用Future API。 若有必要,Dart建立Future对象.
await表达式具备如下形式:
await expression
你能够在一个异步函数中屡次使用 await
表达式. 例如, 下面的代码等了3次函数的结果:
var entrypoint = await findEntrypoint(); var exitCode = await runExecutable(entrypoint, args); await flushThenExit(exitCode);
在await 表达式
中,表达式
的值一般是一个 Future; 若是不是,则该值将自动包装在Future中. 此Future对象表示返回对象的承诺. await 表达式
的值是返回的对象. await 表达式使执行暂停,直到该对象可用.
若是await
不起做用, 请确保它处于异步函数中. 例如, 应用程序的 main()
函数中使用 await
, main()
的正文必须标记为 async
:
main() async { checkVersion(); print('In main: version is ${await lookUpVersion()}'); }
异步for循环具备如下形式:
await for (variable declaration in expression) { // Executes each time the stream emits a value. }
表达式
的值必须具备Stream类型。 执行状况以下:
要中止侦听流,您可使用 break
或 return
语句,该语句突破了for循环,并从Stream中取消订阅.
若是异步for循环不起做用,请确保它处于异步功能 例如,要在应用程序的main()
函数中使用异步for循环 main()
的主体必须标记为 async
:
main() async { ... await for (var request in requestServer) { handleRequest(request); } ... }
有关异步编程的更多信息,请参阅库指南 dart:async . 也能够查看文章 Dart语言异步支持:阶段1和Dart语言异步支持:阶段2, 和 Dart语言规范.
为了让你的Dart类像方法同样被调用,请实现 call()
方法.
在下例中, WannabeFunction
类定义了 call() 方法,该函数须要三个字符串并链接它们, 用空格分开,并附加感叹号. 单击运行按钮 ( ) 执行代码.
class WannabeFunction { call(String a, String b, String c) => '$a $b $c!'; } main() { var wf = new WannabeFunction(); var out = wf("Hi","there,","gang"); print('$out'); }
更多关于将类像函数同样处理, 查看 Dart中的仿真函数.
现代网络浏览器甚至能够在移动平台上运行在多核CPU上。 为了利用全部这些核心,开发人员传统上使用并发运行的共享内存线程。 然而,共享状态并发是容易出错的,可能致使复杂的代码.
而不是线程,全部Dart代码都运行在 isolates内. 每一个隔离区都有本身的内存堆,确保没有任何其余隔离区能够访问隔离区的状态.
在Dart中,函数是对象,就像字符串和数字是对象同样。typedef 或 function-type alias, 给一个函数类型一个别名,当声明字段和返回类型时,可使用该名称。 当函数类型分配给变量时.当函数类型分配给变量时,typedef保留类型信息.
思考下列代码,哪个没有使用 typedef:
class SortedCollection { Function compare; SortedCollection(int f(Object a, Object b)) { compare = f; } } // Initial, broken implementation. int sort(Object a, Object b) => 0; main() { SortedCollection coll = new SortedCollection(sort); // All we know is that compare is a function, // but what type of function? assert(coll.compare is Function); }
当将f
分配给 compare
时类型信息丢失. f
的类型是 (Object,
Object)
→ int
(条件 → 意思是返回), compare
的类型是 Function. 若是咱们更改代码以使用显式名称并保留类型信息,那么开发人员和工具均可以使用该信息.
typedef int Compare(Object a, Object b); class SortedCollection { Compare compare; SortedCollection(this.compare); } // Initial, broken implementation. int sort(Object a, Object b) => 0; main() { SortedCollection coll = new SortedCollection(sort); assert(coll.compare is Function); assert(coll.compare is Compare); }
Note: 目前,typedef仅限于功能类型。 咱们预计这会改变.
由于typedef是简单的别名,它们提供了检查任何函数类型的方法.例如:
typedef int Compare(int a, int b); int sort(int a, int b) => a - b; main() { assert(sort is Compare); // True! }
使用元数据提供有关您的代码的额外信息。 元数据注解以字符 @
开始,以后是对编译时常数(例如 deprecated
)的引用或对常量构造函数的调用.
全部Dart代码都有三个注解: @deprecated
, @override
, 和 @proxy
. 有关使用 @override
和 @proxy
的示例,请参阅扩展一个类. 如下是使用 @deprecated
注解的例子:
class Television { /// _Deprecated: Use [turnOn] instead._ @deprecated void activate() { turnOn(); } /// Turns the TV's power on. void turnOn() { print('on!'); } }
您能够定义本身的元数据注解。 下面是一个定义一个@todo注解的示例,它须要两个参数
library todo; class todo { final String who; final String what; const todo(this.who, this.what); }
如下是使用@todo注解的示例:
import 'todo.dart'; @todo('seth', 'make this do something') void doSomething() { print('do something'); }
元数据能够出如今库,类,typedef,类型参数,构造函数,工厂,函数,字段,参数或变量声明以前,以及 import 或 export 指令以前。 您可使用反射在运行时检索元数据.
Dart 支持单行注释、多行注释、文档注释.
单行注释以 //
开头。 Dart编译器忽略 //
和行尾之间的全部内容.
main() { // TODO: refactor into an AbstractLlamaGreetingFactory? print('Welcome to my Llama farm!'); }
多行注释以/*
开始,以 */
结尾. Dart编译器忽略 /*
和 */
之间的全部内容 (除非注释是文档注释,请参阅下一节). 多行注释能够嵌套.
main() { /* * This is a lot of work. Consider raising chickens. Llama larry = new Llama(); larry.feed(); larry.exercise(); larry.clean(); */ }
文档注释是以 ///
或 /**
开头的多行或单行注释。 在连续行上使用 ///
与多行文档注释具备相同的效果.
在文档注释中,Dart编译器忽略全部文本,除非它包含在括号中。 使用括号,能够参考类,方法,字段,顶级变量,函数和参数。 括号中的名称在已记录的程序元素的词法范围内获得解决.
如下是有关引用其余类和参数的文档注释的示例:
/// A domesticated South American camelid (Lama glama). /// /// Andean cultures have used llamas as meat and pack /// animals since pre-Hispanic times. class Llama { String name; /// Feeds your llama [Food]. /// /// The typical llama eats one bale of hay per week. void feed(Food food) { // ... } /// Exercises your llama with an [activity] for /// [timeLimit] minutes. void exercise(Activity activity, int timeLimit) { // ... } }
在生成的文档中,[Food]
成为Food类的API文档的连接.
要解析Dart代码并生成HTML文档,可使用 文档生成工具. 有关生成的文档的示例,请参阅 Dart API 文档. 有关如何组织您的注释,请参阅 Dart文档注解指南.
本页总结了Dart语言中经常使用的功能。 更多的功能正在实施,但咱们指望他们不会破坏现有的代码。 更多信息查看Dart 语言规范 和 有效的 Dart.
要了解有关Dart核心库的更多信息, 参阅 Dart图书馆之旅.