[Dart]语法基础

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/ 
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:http://www.javashuo.com/article/p-xqjwllar-r.html 
➤若是连接不是山青咏芝的博客园地址,则多是爬取做者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持做者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★javascript

此页面向您展现如何使用每一个主要Dart功能,从变量和运算符到类和库,假设您已经知道如何使用其余语言编程。html

要了解有关Dart核心库的更多信息,请参阅 Dart Libraries之旅。不管什么时候须要有关语言功能的更多详细信息,请参阅Dart语言规范java

提示: 您可使用DartPad播放Dart的大部分语言功能(了解更多信息)。git

打开DartPad程序员

一个基本的Dart项目

如下代码使用了Dart的许多基本功能:github

 1 // Define a function.
 2 printInteger(int aNumber) {
 3   print('The number is $aNumber.'); // Print to console.
 4 }
 5 
 6 // This is where the app starts executing.
 7 main() {
 8   var number = 42; // Declare and initialize a variable.
 9   printInteger(number); // Call a function.
10 }

如下是此程序使用的适用于全部(或几乎全部)Dart应用程序的内容:web

// This is a comment.

单行注释。Dart还支持多行和文档注释。有关详情,请参阅注释正则表达式

int

一种类型。一些其余的内置类型 是StringListbool算法

42

一个字面数字。数字文字是一种编译时常量。express

print()

一种方便的显示输出方式。

'...'(或"..."

字符串文字。

$variableName(或)${expression}

字符串插值:包括字符串文字内部的变量或表达式的字符串。有关更多信息,请参阅 字符串

main()

应用程序执行开始的特殊,必需的顶级功能。有关更多信息,请参阅 main()函数

var

一种声明变量而不指定其类型的方法。

注意: 本网站的代码遵循Dart风格指南中的约定 。

重要的概念

当您了解Dart语言时,请记住如下事实和概念:

  • 您能够放在变量中的全部内容都是一个对象,每一个对象都是一个类的实例。偶数,函数和 null对象。全部对象都继承自Object类。

  • 尽管Dart是强类型的,但类型注释是可选的,由于Dart能够推断类型。在上面的代码中,number 推断为类型int。若是要明确说明不须要任何类型,请 使用特殊类型dynamic

  • Dart支持泛型类型,如List<int>(整数列表)或List<dynamic>(任何类型的对象列表)。

  • Dart支持顶级函数(例如main()),以及绑定到类或对象的函数(分别是静态和实例方法)。您还能够在函数内建立函数(嵌套函数或本地函数)。

  • 相似地,Dart支持顶级变量,以及绑定到类或对象的变量(静态和实例变量)。实例变量有时称为字段或属性。

  • 与Java,Dart不具有关键字publicprotectedprivate。若是标识符如下划线(_)开头,则它对其库是私有的。有关详细信息,请参阅 库和可见性

  • 标识符能够以字母或下划线(_)开头,后跟这些字符加数字的任意组合。

  • Dart有两个表达式(具备运行时值)和 语句(不具备)。例如,条件表达式 condition ? expr1 : expr2的值为expr1expr2。将其与if-else语句进行比较,该语句没有任何值。语句一般包含一个或多个表达式,但表达式不能直接包含语句。

  • Dart工具能够报告两种问题:警告和错误。警告只是代表您的代码可能没法正常工做,但它们不会阻止您的程序执行。错误能够是编译时或运行时。编译时错误会阻止代码执行; 运行时错误致使 代码执行时引起异常

关键词

下表列出了Dart语言专门处理的单词。

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

 

  • 带有上标1的单词是上下文关键字,仅在特定位置具备含义。它们无处不在是有效的标识符。避免使用这些单词做为标识符。可是,若有必要,标有上标的关键字能够是标识符:
  • 带有上标2的单词是内置标识符。为了简化将JavaScript代码移植到Dart的任务,这些关键字在大多数地方都是有效的标识符,但它们不能用做类或类型名称,也不能用做导入前缀。

  • 带有上标3的单词是与Dart 1.0发布后添加的异步支持相关的更新,有限的保留字。不能使用awaityield做为任何函数体中的标识符标记asyncasync*sync*

表中的全部其余单词都是保留字,不能是标识符。

变量

这是建立变量并初始化它的示例:

var name = 'Bob';

变量存储引用。调用的变量name包含对String值为“Bob” 的对象的引用。

name推断变量的类型String,但您能够经过指定它来更改该类型。若是对象不限于单一类型,请按照设计准则指定Objectdynamic类型 。

dynamic name = 'Bob';

另外一种选择是显式声明将推断出的类型:

String name = 'Bob';

注意: 此页面遵循 样式指南建议 使用var,而不是键入注释,用于局部变量。

默认值

未初始化的变量的初始值为null。即便是具备数字类型的变量最初也是null,由于数字 - 就像Dart中的其余全部 - 都是对象。

1 int lineCount;
2 assert(lineCount == null);

注意:assert()生产代码中将忽略 该调用。在开发期间, 除非条件为真,不然抛出异常。有关详细信息,请参阅断言assert(condition)

常量:Final and const

若是您从未打算更改变量,请使用finalconst代替var或替代类型。最终变量只能设置一次; const变量是编译时常量。(Const变量是隐式最终的。)最终的顶级或类变量在第一次使用时被初始化。

注意: 实例变量能够是final但不是const。必须在构造函数体启动以前初始化最终实例变量 - 在变量声明,构造函数参数或构造函数的初始化列表中

如下是建立和设置最终变量的示例:

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

您没法更改最终变量的值:

name = 'Alice'; // Error: a final variable can only be set once.

使用const为您要为变量的编译时间常数。若是const变量在类级别,请标记它static const。在声明变量的地方,将值设置为编译时常量,例如数字或字符串文字,const变量或对常数进行算术运算的结果:

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

const关键字不仅是声明常数变量。您还可使用它来建立常量值,以及声明建立常量值的构造函数。任何变量均可以具备常量值。

1 var foo = const [];
2 final bar = const [];
3 const baz = []; // Equivalent to `const []`

您能够省略声明const的初始化表达式,如上所述。有关详细信息,请参阅不要冗余地使用constconstbaz

您能够更改非final,非const变量的值,即便它曾经有一个const值:

foo = [1, 2, 3]; // Was const []

您没法更改const变量的值:

baz = [42]; // Error: Constant variables can't be assigned a value.

有关使用const建立常量值的更多信息,请参阅 列表Hash集合

内置类型

Dart语言特别支持如下类型:

  • 数字
  • 字符串
  • 布尔
  • 列表(也称为数组)
  • Hash集合
  • 符文(用于表示字符串中的Unicode字符)
  • 符号

您可使用文字初始化任何这些特殊类型的对象。例如,'this is a string'是一个字符串文字,true是一个布尔文字。

由于Dart中的每一个变量都引用一个对象 - 一个类的实例 - 您一般可使用构造函数来初始化变量。一些内置类型有本身的构造函数。例如,您可使用Map()构造函数来建立Hash集合。

数字

Dart号有两种形式:

int

整数值不大于64位,具体取决于平台。在Dart VM上,值能够是-2 63到2 63 - 1.编译为JavaScript的Dart使用JavaScript编号, 容许从-2 53到2 53 - 1的值。

double

64位(双精度)浮点数,由IEEE 754标准规定。

这两个intdouble的亚型num 号码类型包括基本的运算符,如+, - ,/和*,也是在那里你会发现abs()ceil()floor()其余方法中。(按位运算符,例如>>,在int类中定义。)若是num及其子类型没有您要查找的内容,则 dart:math库可能会。

整数是没有小数点的数字。如下是定义整数文字的一些示例:

1 var x = 1;
2 var hex = 0xDEADBEEF;

若是数字包含小数,则为双精度数。如下是定义双重文字的一些示例:

1 var y = 1.1;
2 var exponents = 1.42e5;

从Dart 2.1开始,必要时整数文字会自动转换为双精度数:

double z = 1; // Equivalent to double z = 1.0.

版本说明: 在Dart 2.1以前,在双上下文中使用整数文字是错误的。

如下是将字符串转换为数字的方法,反之亦然:

 1 // String -> int
 2 var one = int.parse('1');
 3 assert(one == 1);
 4 
 5 // String -> double
 6 var onePointOne = double.parse('1.1');
 7 assert(onePointOne == 1.1);
 8 
 9 // int -> String
10 String oneAsString = 1.toString();
11 assert(oneAsString == '1');
12 
13 // double -> String
14 String piAsString = 3.14159.toStringAsFixed(2);
15 assert(piAsString == '3.14');

int类型指定传统的按位移位(<<,>>),AND(&)和OR(|)运算符。例如:

1 assert((3 << 1) == 6); // 0011 << 1 == 0110
2 assert((3 >> 1) == 1); // 0011 >> 1 == 0001
3 assert((3 | 4) == 7); // 0011 | 0100 == 0111

文字数字是编译时常量。许多算术表达式也是编译时常量,只要它们的操做数是编译为数字的编译时常量。

1 const msPerSecond = 1000;
2 const secondsUntilRetry = 5;
3 const msUntilRetry = secondsUntilRetry * msPerSecond;

字符串

Dart字符串是一系列UTF-16代码单元。您可使用单引号或双引号来建立字符串:

1 var s1 = 'Single quotes work well for string literals.';
2 var s2 = "Double quotes work just as well.";
3 var s3 = 'It\'s easy to escape the string delimiter.';
4 var s4 = "It's even easier to use the other delimiter.";

您可使用表达式将表达式的值放在字符串中 。若是表达式是标识符,则能够跳过{}。要获取与对象相对应的字符串,Dart会调用该对象的方法。${expression}.

1 var s = 'string interpolation';
2 
3 assert('Dart has $s, which is very handy.' ==
4     'Dart has string interpolation, ' +
5         'which is very handy.');
6 assert('That deserves all caps. ' +
7         '${s.toUpperCase()} is very handy!' ==
8     'That deserves all caps. ' +
9         'STRING INTERPOLATION is very handy!');

注: 在==两个物体操做测试是不是等价的。若是两个字符串包含相同的代码单元序列,则它们是等效的。

您可使用相邻的字符串文字或+ 运算符来链接字符串:

1 var s1 = 'String '
2     'concatenation'
3     " works even over line breaks.";
4 assert(s1 ==
5     'String concatenation works even over '
6     'line breaks.');
7 
8 var s2 = 'The + operator ' + 'works, as well.';
9 assert(s2 == 'The + operator works, as well.');

建立多行字符串的另外一种方法:使用带有单引号或双引号的三引号:

1 var s1 = '''
2 You can create
3 multi-line strings like this one.
4 ''';
5 
6 var s2 = """This is also a
7 multi-line string.""";

您能够经过为其添加前缀来建立“原始”字符串r

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

有关如何在字符串中表示Unicode字符的详细信息,请参阅Runes

文字字符串是编译时常量,只要任何插值表达式是一个编译时常量,其值为null或数值,字符串或布尔值。

 1 // These work in a const string.
 2 const aConstNum = 0;
 3 const aConstBool = true;
 4 const aConstString = 'a constant string';
 5 
 6 // These do NOT work in a const string.
 7 var aNum = 0;
 8 var aBool = true;
 9 var aString = 'a string';
10 const aConstList = [1, 2, 3];
11 
12 const validConstString = '$aConstNum $aConstBool $aConstString';
13 // const invalidConstString = '$aNum $aBool $aString $aConstList';

有关使用字符串的更多信息,请参阅 字符串和正则表达式

布尔

为了表示布尔值,Dart具备一个名为的类型bool。只有两个对象具备bool类型:boolean literals truefalse,它们都是编译时常量。

Dart的类型安全意味着你不能使用像或 那样的代码 。相反,明确检查值,以下所示:if (nonbooleanValue)assert (nonbooleanValue)

 1 // Check for an empty string.
 2 var fullName = '';
 3 assert(fullName.isEmpty);
 4 
 5 // Check for zero.
 6 var hitPoints = 0;
 7 assert(hitPoints <= 0);
 8 
 9 // Check for null.
10 var unicorn;
11 assert(unicorn == null);
12 
13 // Check for NaN.
14 var iMeantToDoThis = 0 / 0;
15 assert(iMeantToDoThis.isNaN);

Lists

也许几乎每种编程语言中最多见的集合是数组或有序的对象组。在Dart中,数组是 List对象,所以大多数人只是将它们称为列表。

Dart列表文字看起来像JavaScript数组文字。这是一个简单的Dart列表:

var list = [1, 2, 3];

注意: Dart推断出list有类型List<int>。若是尝试将非整数对象添加到此列表,则分析器或运行时会引起错误。有关更多信息,请阅读 类型推断。

列表使用从零开始的索引,其中0是第一个元素list.length - 1的索引,而且是最后一个元素的索引。您能够得到列表的长度并像在JavaScript中同样引用列表元素:

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

要建立一个编译时常量const的列表,请在列表文字前添加:

1 var constantList = const [1, 2, 3];
2 // constantList[1] = 1; // Uncommenting this causes an error.

List类型有许多方便的方法来操做列表。有关列表的更多信息,请参阅泛型和 集合

集:Sets

Dart中的一组是一组无序的独特物品。对集合的Dart支持由set literals和Set类型提供。

版本说明:尽管Set 类型一直是Dart的核心部分,但在Dart 2.2中引入了set literals。

这是一个简单的Dart集,使用set literal建立:

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

注意: Dart推断出halogens具备该类型 Set<String>。若是您尝试向集合中添加错误类型的值,则分析器或运行时会引起错误。有关更多信息,请阅读 类型推断。

要建立一个空集,请使用{}前面带有类型参数,或者指定{}给类型的变量Set

1 var names = <String>{};
2 // Set<String> names = {}; // This works, too.
3 // var names = {}; // Creates a map, not a set.

设置仍是映射? 映射文字的语法相似于集合文字的语法。因为Hash集合文字是第一个,所以{}默认为该Map类型。若是您忘记了类型注释{}或它所分配的变量,则Dart会建立一个类型的对象Map<dynamic, dynamic>

使用add()addAll()方法将项添加到现有集:

1 var elements = <String>{};
2 elements.add('fluorine');
3 elements.addAll(halogens);

使用.length获得的一组项目的数量:

1 var elements = <String>{};
2 elements.add('fluorine');
3 elements.addAll(halogens);
4 assert(elements.length == 5);

要建立一个编译时常量const的集合,请在set literal以前添加:

1 final constantSet = const {
2   'fluorine',
3   'chlorine',
4   'bromine',
5   'iodine',
6   'astatine',
7 };
8 // constantSet.add('helium'); // Uncommenting this causes an error.

有关集的更多信息,请参阅 泛型和 

Hash集合:Maps

一般,映射是关联键和值的对象。键和值均可以是任何类型的对象。每一个键只出现一次,但您能够屡次使用相同的值。Hash集合的Dart支持由Hash集合文字和Map类型提供。

这里有几个简单的Dart贴图,使用贴图文字建立:

 1 var gifts = {
 2   // Key:    Value
 3   'first': 'partridge',
 4   'second': 'turtledoves',
 5   'fifth': 'golden rings'
 6 };
 7 
 8 var nobleGases = {
 9   2: 'helium',
10   10: 'neon',
11   18: 'argon',
12 };

注意: Dart推断出gifts具备类型 Map<String, String>nobleGases类型的 Dart Map<int, String>。若是您尝试将错误类型的值添加到任一映射,则分析器或运行时会引起错误。有关更多信息,请阅读 类型推断。

您可使用Map构造函数建立相同的对象:

1 var gifts = Map();
2 gifts['first'] = 'partridge';
3 gifts['second'] = 'turtledoves';
4 gifts['fifth'] = 'golden rings';
5 
6 var nobleGases = Map();
7 nobleGases[2] = 'helium';
8 nobleGases[10] = 'neon';
9 nobleGases[18] = 'argon';

注意: 您可能但愿看到new Map()而不只仅是Map()。从Dart 2开始,new关键字是可选的。有关详细信息,请参阅使用构造函数

像在JavaScript中同样,将新的键值对添加到现有Hash集合中:

1 var gifts = {'first': 'partridge'};
2 gifts['fourth'] = 'calling birds'; // Add a key-value pair

以与在JavaScript中相同的方式从Hash集合中检索值:

1 var gifts = {'first': 'partridge'};
2 assert(gifts['first'] == 'partridge');

若是您查找不在Hash集合中的键,则会获得null做为回报:

var gifts = { 'first' 'partridge' }; 断言gifts [ 'fifth' ] == null );

使用.length获得的映射中的键值对的数量:

1 var gifts = {'first': 'partridge'};
2 gifts['fourth'] = 'calling birds';
3 assert(gifts.length == 2);

要建立一个编译时常量const的Hash集合,请在Hash集合文字以前添加:

1 final constantMap = const {
2   2: 'helium',
3   10: 'neon',
4   18: 'argon',
5 };
6 
7 // constantMap[2] = 'Helium'; // Uncommenting this causes an error.

有关Hash集合的更多信息,请参阅 泛型和 Hash集合

符文:Runes

在Dart中,符文是字符串的UTF-32代码点。

Unicode为世界上全部书写系统中使用的每一个字母,数字和符号定义惟一的数值。因为Dart字符串是UTF-16代码单元的序列,所以在字符串中表示32位Unicode值须要特殊语法。

表达Unicode代码点的经常使用方法是 \uXXXX,XXXX是4位十六进制值。例如,心脏角色(♥)是\u2665。要指定多于或少于4个十六进制数字,请将值放在大括号中。例如,笑的表情符号(😆)是\u{1f600}

字符串 类有几个属性,你能够用它来提取符文信息。在codeUnitAtcodeUnit属性返回16位编码单元。使用该runes属性获取字符串的符文。

如下示例说明了符文,16位代码单元和32位代码点之间的关系。单击“运行”按钮 以查看运行中的符文。

注意: 使用列表操做操做符文时要当心。这种方法很容易分解,具体取决于特定的语言,字符集和操做。有关更多信息,请参阅 如何在Dart中反转字符串?在Stack Overflow上。

符号

符号对象表示达特程序声明的操做者或标识符。您可能永远不须要使用符号,但它们对于按名称引用标识符的API很是有用,由于缩小会更改标识符名称而不会更改标识符符号。

要获取标识符的符号,请使用符号文字, #后面跟着标识符:

1 #radix
2 #bar

符号文字是编译时常量。 

函数

Dart是一种真正的面向对象语言,所以即便是函数也是对象而且具备类型Function。 这意味着函数能够分配给变量或做为参数传递给其余函数。您也能够调用Dart类的实例,就好像它是一个函数同样。有关详细信息,请参阅可调用类

如下是实现函数的示例:

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

虽然Effective Dart建议 为公共API键入注释,但若是省略类型,该函数仍然有效:

1 isNoble(atomicNumber) {
2   return _nobleGases[atomicNumber] != null;
3 }

对于只包含一个表达式的函数,可使用简写语法:

1 bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

该语法是一个速记 。有时将表示法称为箭头语法。=> expr{ return expr; }=>

注意: 只有表达式 - 不是语句 - 能够出如今箭头(=>)和分号(;)之间。例如,您不能在其中放置if语句,但可使用条件表达式

函数能够有两种类型的参数:必需和可选。首先列出所需参数,而后列出任何可选参数。命名的可选参数也能够标记为@required。有关详细信息,请参阅下一节。

可选参数

可选参数能够是位置参数,也能够是命名参数,但不能同时包

可选的命名参数

调用函数时,可使用指定命名参数 。例如:paramName: value

enableFlags(bold: true, hidden: false);

定义函数时,用于 指定命名参数:{param1, param2, …}

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

Flutter实例建立表达式可能变得复杂,所以窗口小部件构造函数仅使用命名参数。这使得实例建立表达式更易于阅读。

您可使用@required在任何Dart代码(不只仅是Flutter)中注释命名参数, 以指示它是必需参数。例如:

const Scrollbar({Key key, @required Widget child})

当一个Scrollbar构造,分析仪时,报告一个问题 child的说法是不存在的。

必需包中定义。能够package:meta/meta.dart直接导入 ,也能够导入另外一个导出的包 meta,例如Flutter package:flutter/material.dart

可选的位置参数

包装一组函数参数将[]它们标记为可选的位置参数:

1 String say(String from, String msg, [String device]) {
2   var result = '$from says $msg';
3   if (device != null) {
4     result = '$result with a $device';
5   }
6   return result;
7 }

这是一个在没有可选参数的状况下调用此函数的示例:

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

如下是使用第三个参数调用此函数的示例:

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

默认参数值

您的函数可用于=定义命名和位置参数的默认值。默认值必须是编译时常量。若是未提供默认值,则默认值为null

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

1 /// Sets the [bold] and [hidden] flags ...
2 void enableFlags({bool bold = false, bool hidden = false}) {...}
3 
4 // bold will be true; hidden will be false.
5 enableFlags(bold: true);

弃用注释: 旧代码可能使用冒号(:)而不是= 设置命名参数的默认值。缘由是最初只:支持命名参数。该支持可能已被弃用,所以咱们建议您 使用=指定默认值。

下一个示例显示如何设置位置参数的默认值:

 1 String say(String from, String msg,
 2     [String device = 'carrier pigeon', String mood]) {
 3   var result = '$from says $msg';
 4   if (device != null) {
 5     result = '$result with a $device';
 6   }
 7   if (mood != null) {
 8     result = '$result (in a $mood mood)';
 9   }
10   return result;
11 }
12 
13 assert(say('Bob', 'Howdy') ==
14     'Bob says Howdy with a carrier pigeon');

您还能够将列表或Hash集合做为默认值传递。如下示例定义了一个函数,该函数doStuff()指定参数的默认列表和list 参数的默认映射gifts

 1 void doStuff(
 2     {List<int> list = const [1, 2, 3],
 3     Map<String, String> gifts = const {
 4       'first': 'paper',
 5       'second': 'cotton',
 6       'third': 'leather'
 7     }}) {
 8   print('list:  $list');
 9   print('gifts: $gifts');
10 }

main()函数

每一个应用程序都必须具备顶级main()功能,该功能用做应用程序的入口点。该main()函数返回void并具备List<String>参数的可选参数。

如下main()是Web应用程序功能的示例:

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

注意:..前面代码中 的语法称为级联。使用级联,您能够对单个对象的成员执行多个操做。

如下main()是带参数的命令行应用程序的函数示例:

1 // Run the app like this: dart args.dart 1 test
2 void main(List<String> arguments) {
3   print(arguments);
4 
5   assert(arguments.length == 2);
6   assert(int.parse(arguments[0]) == 1);
7   assert(arguments[1] == 'test');
8 }

您可使用args库来定义和解析命令行参数。

做为第一类对象的功能

您能够将函数做为参数传递给另外一个函数。例如:

1 void printElement(int element) {
2   print(element);
3 }
4 
5 var list = [1, 2, 3];
6 
7 // Pass printElement as a parameter.
8 list.forEach(printElement);

您还能够为变量分配函数,例如:

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

此示例使用匿名函数。更多关于下一节的内容。

匿名函数

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

匿名函数看起来相似于命名函数 - 零个或多个参数,在逗号和括号之间用逗号和可选类型注释分隔。

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

1 ([[Type] param1[, …]]) { 
2   codeBlock; 
3 }; 

如下示例使用无类型参数定义匿名函数item。为列表中的每一个项调用的函数将打印一个包含指定索引处的值的字符串。

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

单击运行按钮以执行代码。

若是函数只包含一个语句,则可使用箭头表示法缩短它。将如下行粘贴到DartPad中,而后单击“运行”以验证它是否在功能上等效。

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

词汇范围

Dart是一种词法范围的语言,这意味着变量的范围是静态肯定的,只需经过代码的布局。您能够“向外跟随花括号”以查看变量是否在范围内。

如下是每一个范围级别包含变量的嵌套函数示例:

 1 bool topLevel = true;
 2 
 3 void main() {
 4   var insideMain = true;
 5 
 6   void myFunction() {
 7     var insideFunction = true;
 8 
 9     void nestedFunction() {
10       var insideNestedFunction = true;
11 
12       assert(topLevel);
13       assert(insideMain);
14       assert(insideFunction);
15       assert(insideNestedFunction);
16     }
17   }
18 }

请注意如何nestedFunction()使用每一个级别的变量,一直到顶级。

词汇封闭

甲闭合是可以访问在其词法范围的变量的函数的对象,即便当函数用于其原来的范围以外。

函数能够关闭周围范围中定义的变量。在如下示例中,makeAdder()捕获变量addBy。不管返回的函数在哪里,它都会记住addBy

 1 /// Returns a function that adds [addBy] to the
 2 /// function's argument.
 3 Function makeAdder(num addBy) {
 4   return (num i) => addBy + i;
 5 }
 6 
 7 void main() {
 8   // Create a function that adds 2.
 9   var add2 = makeAdder(2);
10 
11   // Create a function that adds 4.
12   var add4 = makeAdder(4);
13 
14   assert(add2(3) == 5);
15   assert(add4(3) == 7);

测试函数是否相等

如下是测试顶级函数,静态方法和实例方法的相等性的示例:

 1 void foo() {} // A top-level function
 2 
 3 class A {
 4   static void bar() {} // A static method
 5   void baz() {} // An instance method
 6 }
 7 
 8 void main() {
 9   var x;
10 
11   // Comparing top-level functions.
12   x = foo;
13   assert(foo == x);
14 
15   // Comparing static methods.
16   x = A.bar;
17   assert(A.bar == x);
18 
19   // Comparing instance methods.
20   var v = A(); // Instance #1 of A
21   var w = A(); // Instance #2 of A
22   var y = w;
23   x = w.baz;
24 
25   // These closures refer to the same instance (#2),
26   // so they're equal.
27   assert(y.baz == x);
28 
29   // These closures refer to different instances,
30   // so they're unequal.
31   assert(v.baz != w.baz);
32 }

返回值

全部函数都返回一个值。若是未指定返回值,则将语句return null;隐式附加到函数体。

1 foo() {}
2 
3 assert(foo() == null);

运算符

Dart定义下表中显示的运算符。您能够覆盖许多运营商,如在 可重写运营商

描述 操做者
一元后缀 expr++    expr--    ()    []    .    ?.
一元前缀 -expr    !expr    ~expr    ++expr    --expr   
*    /    %  ~/
添加剂 +    -
转移 <<    >>    >>>
按位AND &
按位异或 ^
按位OR |
关系和类型测试 >=    >    <=    <    as    is    is!
平等 ==    !=   
逻辑AND &&
逻辑或 ||
若是为null ??
有条件的 expr1 ? expr2 : expr3
级联 ..
分配 =    *=    /=   +=   -=   &=   ^=   等等

警告: 运算符优先级是Dart解析器行为的近似值。有关明确的答案,请参阅Dart语言规范中的语法 。

使用运算符时,能够建立表达式。如下是运算符表达式的一些示例:

1 a++
2 a + b
3 a = b
4 a == b
5 c ? a : b
6 a is T

运算符表中,每一个运算符的优先级高于其后的行中的运算符。例如,乘法运算符的%优先级高于(所以以前执行)等于运算符==,它的优先级高于逻辑AND运算符&&。该优先级意味着如下两行代码执行相同的方式:

1 // Parentheses improve readability.
2 if ((n % i == 0) && (d % i == 0)) ...
3 
4 // Harder to read, but equivalent.
5 if (n % i == 0 && d % i == 0) ...

警告: 对于处理两个操做数的运算符,最左边的操做数肯定使用哪一个版本的运算符。例如,若是您有Vector对象和Point对象,则aVector + aPoint使用Vector版本的+。

算术运算符

Dart支持一般的算术运算符,以下表所示。

操做者 含义
+
减去
-expr 一元减号,也称为否认(反转表达式的符号)
*
/ 划分
~/ 除以,返回整数结果
% 获取整数除法的余数(模数)

例:

1 assert(2 + 3 == 5);
2 assert(2 - 3 == -1);
3 assert(2 * 3 == 6);
4 assert(5 / 2 == 2.5); // Result is a double
5 assert(5 ~/ 2 == 2); // Result is an int
6 assert(5 % 2 == 1); // Remainder
7 
8 assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');

Dart还支持前缀和后缀增量和减量运算符。

操做者 含义
++var var = var + 1(表达式值是var + 1
var++ var = var + 1(表达式值是var
--var var = var – 1(表达式值是var – 1
var-- var = var – 1(表达式值是var

例:

 1 var a, b;
 2 
 3 a = 0;
 4 b = ++a; // Increment a before b gets its value.
 5 assert(a == b); // 1 == 1
 6 
 7 a = 0;
 8 b = a++; // Increment a AFTER b gets its value.
 9 assert(a != b); // 1 != 0
10 
11 a = 0;
12 b = --a; // Decrement a before b gets its value.
13 assert(a == b); // -1 == -1
14 
15 a = 0;
16 b = a--; // Decrement a AFTER b gets its value.
17 assert(a != b); // -1 != 0

平等和关系运营商

下表列出了相等运算符和关系运算符的含义。

操做者 含义
== 等于; 见下面的讨论
!= 不相等
> 比...更棒
< 少于
>= 大于或等于
<= 小于或等于

要测试两个对象x和y是否表示相同的事物,请使用 ==运算符。(在极少数状况下,您须要知道两个对象是不是彻底相同的对象,请使用相同的() 函数。)如下是==运算符的工做方式:

  1. 若是x或y为null,则若是二者都为null则返回true;若是只有一个为null,则返回false。

  2. 返回方法调用的结果 。(这是正确的,运算符,例如在第一个操做数上调用的方法。您甚至能够覆盖许多运算符,包括,正如您在Overridable运算符中看到的那样 。)x.==(y)====

这是使用每一个相等和关系运算符的示例:

1 assert(2 == 2);
2 assert(2 != 3);
3 assert(3 > 2);
4 assert(2 < 3);
5 assert(3 >= 3);
6 assert(2 <= 3);

键入测试运算符

使用asisis!运算符能够方便地在运行时检查类型。

操做者 含义
as Typecast(也用于指定库前缀
is 若是对象具备指定的类型,则为True
is! 若是对象具备指定的类型,则返回false

obj is T若是obj实现了指定的接口,则结果为true T。例如,obj is Object老是如此。

使用as运算符将对象强制转换为特定类型。一般,您应该使用它做为is对使用该对象的表达式后跟对象的测试的简写。例如,请考虑如下代码:

1 if (emp is Person) {
2   // Type check
3   emp.firstName = 'Bob';
4 }

您可使用as运算符缩短代码:

(emp as Person).firstName = 'Bob';

注意: 代码不相同。若是emp为null或不是Person,则第一个示例(with is)不执行任何操做; 第二个(带as)抛出一个异常。

分配运营商

如您所见,您可使用=运算符分配值。要仅在assign-to变量为null时分配,请使用??=运算符。

1 // Assign value to a
2 a = value;
3 // Assign value to b if b is null; otherwise, b stays the same
4 b ??= value;

复合赋值运算符,例如+=将操做与赋值组合在一块儿。

= –= /= %= >>= ^=
+= *= ~/= <<= &= |=

如下是复合赋值运算符的工做原理:

  复合赋值 等价表达
对于运营商op: a op= b a = a op b
例: a += b a = a + b

如下示例使用赋值和复合赋值运算符:

1 var a = 2; // Assign using =
2 a *= 3; // Assign and multiply: a = a * 3
3 assert(a == 6);

逻辑运算符

您可使用逻辑运算符反转或组合布尔表达式。

操做者 含义
!expr 反转如下表达式(将false更改成true,反之亦然)
|| 逻辑或
&& 逻辑AND

如下是使用逻辑运算符的示例:

1 if (!done && (col == 0 || col == 3)) {
2   // ...Do something...
3 }

按位和移位运算符

您能够在Dart中操纵数字的各个位。一般,您将使用这些按位和移位运算符和整数。

操做者 含义
&
| 要么
^ XOR
~expr 一元逐位补码(0s变为1s; 1s变为0s)
<< 向左转
>> 向右转

这是使用按位和移位运算符的示例:

1 final value = 0x22;
2 final bitmask = 0x0f;
3 
4 assert((value & bitmask) == 0x02); // AND
5 assert((value & ~bitmask) == 0x20); // AND NOT
6 assert((value | bitmask) == 0x2f); // OR
7 assert((value ^ bitmask) == 0x2d); // XOR
8 assert((value << 4) == 0x220); // Shift left
9 assert((value >> 4) == 0x02); // Shift right

条件表达式

Dart有两个运算符,可让您简明地计算可能须要if-else语句的表达式:

condition ? expr1 : expr2
若是condition为true,则计算expr1(并返回其值); 不然,计算并返回expr2的值。
expr1 ?? expr2
若是expr1为非null,则返回其值; 不然,计算并返回expr2的值。

当您须要根据布尔表达式分配值时,请考虑使用?:

var visibility = isPublic ? 'public' : 'private';

若是布尔表达式测试为null,请考虑使用??

String playerName(String name) => name ?? 'Guest';

前面的例子至少能够用其余两种方式编写,但不能简洁:

 1 // Slightly longer version uses ?: operator.
 2 String playerName(String name) => name != null ? name : 'Guest';
 3 
 4 // Very long version uses if-else statement.
 5 String playerName(String name) {
 6   if (name != null) {
 7     return name;
 8   } else {
 9     return 'Guest';
10   }
11 }

级联符号(..)

Cascades(..)容许您对同一对象进行一系列操做。除了函数调用,您还能够访问同一对象上的字段。这一般能够为您节省建立临时变量的步骤,并容许您编写更多流畅的代码。

请考虑如下代码:

1 querySelector('#confirm') // Get an object.
2   ..text = 'Confirm' // Use its members.
3   ..classes.add('important')
4   ..onClick.listen((e) => window.alert('Confirmed!'));

第一个方法调用,querySelector()返回一个选择器对象。级联表示法后面的代码对此选择器对象进行操做,忽略可能返回的任何后续值。

前面的例子至关于:

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

您也能够嵌套您的级联。例如:

1 final addressBook = (AddressBookBuilder()
2       ..name = 'jenny'
3       ..email = 'jenny@example.com'
4       ..phone = (PhoneNumberBuilder()
5             ..number = '415-555-0100'
6             ..label = 'home')
7           .build())
8     .build();

当心在返回实际对象的函数上构造级联。例如,如下代码失败:

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

sb.write()调用返回无效的,并不能构造上的级联void

注意: 严格来讲,级联的“双点”符号不是运算符。它只是Dart语法的一部分。

其余运营商

在其余示例中,您已经看到了大多数剩余的运算符:

操做者 名称 含义
() 功能应用 表示函数调用
[] 列表访问 引用列表中指定索引处的值
. 会员访问权限 指表达式的属性; 示例:从表达式中foo.bar选择属性barfoo
?. 有条件的成员访问权限 好比.,但最左边的操做数能够为null; 示例:从表达式中foo?.bar选择属性barfoo除非foo为null(在这种状况下,值为foo?.barnull)

有关详细信息.?.以及..运营商,见 

控制流程语句

您可使用如下任一方法控制Dart代码的流程:

  • if 和 else
  • for 循环
  • whiledowhile循环
  • break 和 continue
  • switch 和 case
  • assert

您还可使用try-catch和影响控制流throw,如异常中所述

若是是,不然

Dart支持if带有可选else语句的语句,以下一个示例所示。另见条件表达式

1 if (isRaining()) {
2   you.bringRainCoat();
3 } else if (isSnowing()) {
4   you.wearJacket();
5 } else {
6   car.putTopDown();
7 }

与JavaScript不一样,条件必须使用布尔值,没有别的。有关更多信息,请参阅 布尔值。

For循环

您可使用标准for循环进行迭代。例如:

1 var message = StringBuffer('Dart is fun');
2 for (var i = 0; i < 5; i++) {
3   message.write('!');
4 }

Dart for循环内部的闭包捕获了索引的值,避免了JavaScript中常见的陷阱。例如,考虑:

1 var callbacks = [];
2 for (var i = 0; i < 2; i++) {
3   callbacks.add(() => print(i));
4 }
5 callbacks.forEach((c) => c());

0而后1,正如预期的那样输出。相反,该示例将打印2,而后2在JavaScript中。

若是要迭代的对象是Iterable,则可使用 forEach()方法。forEach()若是您不须要知道当前的迭代计数器,则使用是一个不错的选择:

candidates.forEach((candidate) => candidate.interview());

像List和Set这样for-in的可迭代类也支持迭代的形式 :

1 var collection = [0, 1, 2];
2 for (var x in collection) {
3   print(x); // 0 1 2
4 }

while 和 do-while

一个while循环计算循环前的状态:

1 while (!isDone()) {
2   doSomething();
3 }

dowhile循环评估循环后的条件:

1 do {
2   printLine();
3 } while (!atEndOfPage());

break 和 continue

使用break中止循环:

1 while (true) {
2   if (shutDownRequested()) break;
3   processIncomingRequests();
4 }

使用continue跳到下一个循环迭代:

1 for (int i = 0; i < candidates.length; i++) {
2   var candidate = candidates[i];
3   if (candidate.yearsExperience < 5) {
4     continue;
5   }
6   candidate.interview();
7 }

若是您使用Iterable(如列表或集合),则可能会以不一样的方式编写该示例 :

1 candidates
2     .where((c) => c.yearsExperience >= 5)
3     .forEach((c) => c.interview());

siwtch 和 case

Dart中的switch语句使用比较整数,字符串或编译时常量==。比较对象必须都是同一个类的实例(而不是其任何子类型),而且该类不能覆盖==。 枚举类型switch语句中运行良好。

注意: Dart中的Switch语句适用于有限的状况,例如解释器或扫描仪。

每一个非空case子句break一般以语句结束。其余有效的方式来结束一个非空的case条款是continue, throwreturn声明。

default当没有case子句匹配时,使用子句执行代码:

 1 var command = 'OPEN';
 2 switch (command) {
 3   case 'CLOSED':
 4     executeClosed();
 5     break;
 6   case 'PENDING':
 7     executePending();
 8     break;
 9   case 'APPROVED':
10     executeApproved();
11     break;
12   case 'DENIED':
13     executeDenied();
14     break;
15   case 'OPEN':
16     executeOpen();
17     break;
18   default:
19     executeUnknown();
20 }

如下示例省略breakcase子句中的语句,从而生成错误:

 1 var command = 'OPEN';
 2 switch (command) {
 3   case 'OPEN':
 4     executeOpen();
 5     // ERROR: Missing break
 6 
 7   case 'CLOSED':
 8     executeClosed();
 9     break;
10 }

可是,Dart确实支持空case句子,容许一种形式的落空:

1 var command = 'CLOSED';
2 switch (command) {
3   case 'CLOSED': // Empty case falls through.
4   case 'NOW_CLOSED':
5     // Runs for both CLOSED and NOW_CLOSED.
6     executeNowClosed();
7     break;
8 }

若是你真的想要fall-through,你可使用一个continue声明和一个标签:

 1 var command = 'CLOSED';
 2 switch (command) {
 3   case 'CLOSED':
 4     executeClosed();
 5     continue nowClosed;
 6   // Continues executing at the nowClosed label.
 7 
 8   nowClosed:
 9   case 'NOW_CLOSED':
10     // Runs for both CLOSED and NOW_CLOSED.
11     executeNowClosed();
12     break;
13 }

一个case条款能够有局部变量,只有内部的条款的范围是可见的。

断言:Assert

assert若是布尔条件为false,则使用语句来中断正常执行。您能够在本导览中找到断言语句的示例。这里还有一些:

1 // Make sure the variable has a non-null value.
2 assert(text != null);
3 
4 // Make sure the value is less than 100.
5 assert(number < 100);
6 
7 // Make sure this is an https URL.
8 assert(urlString.startsWith('https'));

注意: 断言语句对生产代码没有影响; 他们只是为了发展。Flutter在调试模式下启用断言 仅限开发的工具(如dartdevc) 一般默认支持断言。一些工具,如dartdart2js, 经过命令行标志支持断言:--enable-asserts

要将消息附加到断言,请添加一个字符串做为第二个参数。

1 assert(urlString.startsWith('https'),
2     'URL ($urlString) should start with "https".');

第一个参数assert能够是任何解析为布尔值的表达式。若是表达式的值为true,则断言成功并继续执行。若是为false,则断言失败并抛出异常( AssertionError)。

异常:Exceptions

您的Dart代码能够抛出并捕获异常。例外是指示发生意外事件的错误。若是未捕获异常,则会引起引起异常的隔离,而且一般会隔离隔离及其程序。

与Java相比,Dart的全部异常都是未经检查的异常。方法不会声明它们可能引起的异常,而且您不须要捕获任何异常。

Dart提供了ExceptionError 类型,以及许多预约义的子类型。固然,您能够定义本身的例外状况。可是,Dart程序能够抛出任何非null对象 - 不只仅是Exception和Error对象 - 做为例外。

throw

如下是抛出或引起异常的示例:

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

你也能够抛出任意对象:

throw 'Out of llamas!';

 

注意:生产质量代码一般会抛出实现错误异常的类型 。

由于抛出异常是一个表达式,因此能够在=>语句中以及容许表达式的任何其余地方抛出异常:

void distanceTo(Point other) => throw UnimplementedError();

 

Catch

捕获或捕获异常会阻止异常传播(除非您从新抛出异常)。捕获异常使您有机会处理它:

1 try {
2   breedMoreLlamas();
3 } on OutOfLlamasException {
4   buyMoreLlamas();
5 }

要处理可能抛出多种类型异常的代码,能够指定多个catch子句。与抛出对象的类型匹配的第一个catch子句处理异常。若是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 }

正如上面的代码所示,您可使用oncatch或二者兼而有之。使用on时须要指定异常类型。使用catch时,你的异常处理程序须要异常对象。

您能够指定一个或两个参数catch()。第一个是抛出的异常,第二个是堆栈跟踪(StackTrace对象)。

1 try {
2   // ···
3 } on Exception catch (e) {
4   print('Exception details:\n $e');
5 } catch (e, s) {
6   print('Exception details:\n $e');
7   print('Stack trace:\n $s');
8 }

要部分处理异常,同时容许它传播,请使用rethrow关键字。

 1 void misbehave() {
 2   try {
 3     dynamic foo = true;
 4     print(foo++); // Runtime error
 5   } catch (e) {
 6     print('misbehave() partially handled ${e.runtimeType}.');
 7     rethrow; // Allow callers to see the exception.
 8   }
 9 }
10 
11 void main() {
12   try {
13     misbehave();
14   } catch (e) {
15     print('main() finished handling ${e.runtimeType}.');
16   }
17 }

finally

不管是否抛出异常,要确保某些代码运行,请使用finally子句。若是没有catch子句匹配该异常,则在finally子句运行后传播异常:

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

finally子句在任何匹配的catch子句以后运行:

1 try {
2   breedMoreLlamas();
3 } catch (e) {
4   print('Error: $e'); // Handle the exception first.
5 } finally {
6   cleanLlamaStalls(); // Then clean up.
7 }

阅读 Libraries之旅的例外部分,了解更多信息 。

Dart是一种面向对象的语言,具备类和基于mixin的继承。每一个对象都是一个类的实例,全部类都来自Object。 基于Mixin的继承意味着虽然每一个类(除了Object)只有一个超类,可是类体能够在多个类层次结构中重用。

使用班级成员

对象具备由函数和数据(分别为方法和 实例变量)组成的成员。调用方法时,能够 在对象上调用它:该方法能够访问该对象的函数和数据。

使用点(.)来引用实例变量或方法:

 1 var p = Point(2, 2);
 2 
 3 // Set the value of the instance variable y.
 4 p.y = 3;
 5 
 6 // Get the value of y.
 7 assert(p.y == 3);
 8 
 9 // Invoke distanceTo() on p.
10 num distance = p.distanceTo(Point(4, 4));

当最左边的操做数为null时,使用?.而不是.避免异常:

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

使用构造函数

您可使用构造函数建立对象。构造函数名称能够是ClassName或 。例如,如下代码使用和构造函数建立对象:ClassName.identifierPointPoint()Point.fromJson()

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

如下代码具备相同的效果,但new在构造函数名称以前使用可选关键字:

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

版本注:该new关键字Dart2成为可选项。

有些类提供常量构造函数。要使用常量构造函数建立编译时常量,请将const关键字放在构造函数名称以前:

var p = const ImmutablePoint(2, 2);

构造两个相同的编译时常量会产生一个规范的实例:

1 var a = const ImmutablePoint(1, 1);
2 var b = const ImmutablePoint(1, 1);
3 
4 assert(identical(a, b)); // They are the same instance!

在常量上下文中,您能够省略const构造函数或文字以前的内容。例如,查看此代码,该代码建立一个const映射:

//这里有不少const关键字。常量pointAndLine = const的{ '点' 常量[ 常量ImmutablePoint 0 0 )],'线' 常数[ 常量ImmutablePoint 1 10 ),常量ImmutablePoint ( - 2 11 )],};

您能够省略除const关键字的第一次使用以外的全部内容:

1 // Lots of const keywords here.
2 const pointAndLine = const {
3   'point': const [const ImmutablePoint(0, 0)],
4   'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
5 };

若是常量构造函数在常量上下文以外而且在没有const它的状况下调用,则会建立一个很是量对象:

1 var a = const ImmutablePoint(1, 1); // Creates a constant
2 var b = ImmutablePoint(1, 1); // Does NOT create a constant
3 
4 assert(!identical(a, b)); // NOT the same instance!

版本注:该const关键字成为Dart2的恒定范围内可选。

获取对象的类型

要在运行时获取对象的类型,可使用Object的runtimeType属性,该属性返回Type对象。

print 'a的类型是$ {a.runtimeType}' );

到目前为止,您已经了解了如何使用类。本节的其他部分将介绍如何实现类。

实例变量

如下是声明实例变量的方法:

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

全部未初始化的实例变量都具备该值null

全部实例变量都生成一个隐式getter方法。非最终实例变量也会生成隐式setter方法。有关详细信息,请参阅Getters和setter

1 class Point {
2   num x; // Declare instance variable x, initially null.
3   num y; // Declare y, initially null.
4   num z = 0; // Declare z, initially 0.
5 }

若是初始化声明它的实例变量(而不是构造函数或方法),则在建立实例时设置该值,该实例在构造函数及其初始化列表执行以前。

构造函数

经过建立与其类同名的函数来声明构造函数(另外,可选地,如命名构造函数中所述的附加标识符 )。最多见的构造函数形式,即生成构造函数,建立一个类的新实例:

 1 class Point {
 2   num x;
 3   num y;
 4 }
 5 
 6 void main() {
 7   var point = Point();
 8   point.x = 4; // Use the setter method for x.
 9   assert(point.x == 4); // Use the getter method for x.
10   assert(point.y == null); // Values default to null.
11 }

this关键字是指当前实例。

注意: 使用this时,才会有一个名称冲突。不然,Dart风格省略了this

将构造函数参数赋值给实例变量的模式是如此常见,Dart具备语法糖,使其变得简单:

1 class Point {
2   num x, y;
3 
4   Point(num x, num y) {
5     // There's a better way to do this, stay tuned.
6     this.x = x;
7     this.y = y;
8   }
9 }

默认构造函数

若是您未声明构造函数,则会为您提供默认构造函数。默认构造函数没有参数,并在超类中调用无参数构造函数。

构造函数不是继承的

子类不从其超类继承构造函数。声明没有构造函数的子类只有默认(无参数,无名称)构造函数。

命名构造函数

使用命名构造函数为类实现多个构造函数或提供额外的清晰度:

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

请记住,构造函数不是继承的,这意味着超类的命名构造函数不会被子类继承。若是但愿使用超类中定义的命名构造函数建立子类,则必须在子类中实现该构造函数。

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

默认状况下,子类中的构造函数调用超类的未命名的无参数构造函数。超类的构造函数在构造函数体的开头被调用。若是 还使用初始化列表,则在调用超类以前执行。总之,执行顺序以下:

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

若是超类没有未命名的无参数构造函数,则必须手动调用超类中的一个构造函数。在冒号(:)以后,在构造函数体(若是有)以前指定超类构造函数。

在下面的示例中,Employee类的构造函数为其超类Person调用命名构造函数。单击运行按钮以执行代码。

由于在调用构造函数以前会计算超类构造函数的参数,因此参数能够是一个表达式,例如函数调用:

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

警告: 超类构造函数的参数无权访问this。例如,参数能够调用静态方法,但不能调用实例方法。

初始化list

除了调用超类构造函数以外,还能够在构造函数体运行以前初始化实例变量。用逗号分隔初始化程序。

1 // Initializer list sets instance variables before
2 // the constructor body runs.
3 Point.fromJson(Map<String, num> json)
4     : x = json['x'],
5       y = json['y'] {
6   print('In Point.fromJson(): ($x, $y)');
7 }

警告: 初始化程序的右侧无权访问this

在开发期间,您能够经过assert在初始化列表中使用来验证输入。

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

设置最终字段时,初始化程序列表很方便。如下示例初始化初始化列表中的三个最终字段。单击运行按钮以执行代码。

重定向构造函数

有时构造函数的惟一目的是重定向到同一个类中的另外一个构造函数。重定向构造函数的主体是空的,构造函数调用出如今冒号(:)以后。

1 class Point {
2   num x, y;
3 
4   // The main constructor for this class.
5   Point(this.x, this.y);
6 
7   // Delegates to the main constructor.
8   Point.alongXAxis(num x) : this(x, 0);
9 }

常量构造函数

若是您的类生成永远不会更改的对象,则可使这些对象成为编译时常量。为此,请定义const构造函数并确保全部实例变量都是final

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

常量构造函数并不老是建立常量。有关详细信息,请参阅有关使用构造函数的部分 。

工厂构造函数:Factory constructors

factory在实现不老是建立其类的新实例的构造函数时使用关键字。例如,工厂构造函数可能从缓存中返回实例,或者它可能返回子类型的实例。

如下示例演示了从缓存中返回对象的工厂构造函数:

 1 class Logger {
 2   final String name;
 3   bool mute = false;
 4 
 5   // _cache is library-private, thanks to
 6   // the _ in front of its name.
 7   static final Map<String, Logger> _cache =
 8       <String, Logger>{};
 9 
10   factory Logger(String name) {
11     if (_cache.containsKey(name)) {
12       return _cache[name];
13     } else {
14       final logger = Logger._internal(name);
15       _cache[name] = logger;
16       return logger;
17     }
18   }
19 
20   Logger._internal(this.name);
21 
22   void log(String msg) {
23     if (!mute) print(msg);
24   }
25 }

注意: 工厂构造函数无权访问this

像调用任何其余构造函数同样调用工厂构造函数:

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

方法

方法是为对象提供行为的函数。

实例方法

对象的实例方法能够访问实例变量和this。在distanceTo()下面的示例中方法是一个实例方法的一个例子:

 1 import 'dart:math';
 2 
 3 class Point {
 4   num x, y;
 5 
 6   Point(this.x, this.y);
 7 
 8   num distanceTo(Point other) {
 9     var dx = x - other.x;
10     var dy = y - other.y;
11     return sqrt(dx * dx + dy * dy);
12   }
13 }

Getters 和 setters

getter和setter是提供对象属性的读写访问权限的特殊方法。回想一下,每一个实例变量都有一个隐式getter,若是合适的话还有一个setter。您可使用getset关键字经过实现getter和setter来建立其余属性 :

 1 class Rectangle {
 2   num left, top, width, height;
 3 
 4   Rectangle(this.left, this.top, this.width, this.height);
 5 
 6   // Define two calculated properties: right and bottom.
 7   num get right => left + width;
 8   set right(num value) => left = value - width;
 9   num get bottom => top + height;
10   set bottom(num value) => top = value - height;
11 }
12 
13 void main() {
14   var rect = Rectangle(3, 4, 20, 15);
15   assert(rect.left == 3);
16   rect.right = 12;
17   assert(rect.left == -8);
18 }

使用getter和setter,您能够从实例变量开始,稍后使用方法包装它们,而无需更改客户端代码。

注意: 不管是否明肯定义了getter,增量(++)等运算符都以预期的方式工做。为避免任何意外的反作用,操做员只需调用一次getter,将其值保存在临时变量中。

抽象方法

实例,getter和setter方法能够是抽象的,定义一个接口,但将其实现留给其余类。抽象方法只能存在于抽象类中

要使方法成为抽象,请使用分号(;)而不是方法体:

 1 abstract class Doer {
 2   // Define instance variables and methods...
 3 
 4   void doSomething(); // Define an abstract method.
 5 }
 6 
 7 class EffectiveDoer extends Doer {
 8   void doSomething() {
 9     // Provide an implementation, so the method is not abstract here...
10   }
11 }

抽象类

使用abstract修饰符定义抽象类 - 没法实例化的类。抽象类对于定义接口很是有用,一般还有一些实现。若是但愿抽象类看起来是可实例化的,请定义工厂构造函数

抽象类一般有抽象方法。这是一个声明具备抽象方法的抽象类的示例:

1 // This class is declared abstract and thus
2 // can't be instantiated.
3 abstract class AbstractContainer {
4   // Define constructors, fields, methods...
5 
6   void updateChildren(); // Abstract method.
7 }

隐式接口

每一个类都隐式定义一个接口,该接口包含该类的全部实例成员及其实现的任何接口。若是要在不继承B实现的状况下建立支持B类API的A类,则A类应实现B接口。

类经过在implements子句中声明它们而后提供接口所需的API来实现一个或多个 接口。例如:

 1 // A person. The implicit interface contains greet().
 2 class Person {
 3   // In the interface, but visible only in this library.
 4   final _name;
 5 
 6   // Not in the interface, since this is a constructor.
 7   Person(this._name);
 8 
 9   // In the interface.
10   String greet(String who) => 'Hello, $who. I am $_name.';
11 }
12 
13 // An implementation of the Person interface.
14 class Impostor implements Person {
15   get _name => '';
16 
17   String greet(String who) => 'Hi $who. Do you know who I am?';
18 }
19 
20 String greetBob(Person person) => person.greet('Bob');
21 
22 void main() {
23   print(greetBob(Person('Kathy')));
24   print(greetBob(Impostor()));
25 }

这是一个指定类实现多个接口的示例:

class Point implements Comparable, Location {...}

扩展类

使用extends建立一个子类,并super指超:

 1 class Television {
 2   void turnOn() {
 3     _illuminateDisplay();
 4     _activateIrSensor();
 5   }
 6   // ···
 7 }
 8 
 9 class SmartTelevision extends Television {
10   void turnOn() {
11     super.turnOn();
12     _bootNetworkInterface();
13     _initializeMemory();
14     _upgradeApps();
15   }
16   // ···
17 }

覆盖成员:Overriding members

子类能够覆盖实例方法,getter和setter。您可使用@override注释来指示您有意覆盖成员:

1 class SmartTelevision extends Television {
2   @override
3   void turnOn() {...}
4   // ···
5 }

要在类型安全的代码中缩小方法参数或实例变量的 类型,可使用covariant关键字

可覆盖的运算符

您能够覆盖下表中显示的运算符。例如,若是定义Vector类,则能够定义+添加两个向量的方法。

< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
% >>  

注意:您可能已经注意到,这!=不是可覆盖的运算符。表达e1 != e2只是语法糖!(e1 == e2)

这是一个覆盖+-运算符的类的示例:

 1 class Vector {
 2   final int x, y;
 3 
 4   Vector(this.x, this.y);
 5 
 6   Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
 7   Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
 8 
 9   // Operator == and hashCode not shown. For details, see note below.
10   // ···
11 }
12 
13 void main() {
14   final v = Vector(2, 3);
15   final w = Vector(2, 2);
16 
17   assert(v + w == Vector(4, 5));
18   assert(v - w == Vector(0, 1));
19 }

若是覆盖==,则还应覆盖Object的hashCodegetter。用于覆盖的一个例子==hashCode,请参见 实施映射键

有关覆盖的更多信息,请参阅 扩展类

noSuchMethod()

要在代码尝试使用不存在的方法或实例变量时检测或作出反应,您能够覆盖noSuchMethod()

1 class A {
2   // Unless you override noSuchMethod, using a
3   // non-existent member results in a NoSuchMethodError.
4   @override
5   void noSuchMethod(Invocation invocation) {
6     print('You tried to use a non-existent member: ' +
7         '${invocation.memberName}');
8   }
9 }

你不能调用,除非未实现的方法, 一个如下是真实的:

  • 接收器具备静态类型dynamic

  • 接收器有一个静态类型,它定义了未实现的方法(抽象是OK),接收器的动态类型的实现与类noSuchMethod() 中的实现不一样Object

有关更多信息,请参阅非正式 noSuchMethod转发规范。

 

枚举类型

枚举类型(一般称为枚举或枚举)是一种特殊类,用于表示固定数量的常量值。

使用枚举

使用enum关键字声明枚举类型:

enum Color { red, green, blue }

枚举中的每一个值都有一个indexgetter,它返回枚举声明中值的从零开始的位置。例如,第一个值具备索引0,第二个值具备索引1。

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

要获取枚举中全部值的列表,请使用枚举values常量。

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

您能够在switch语句中使用枚举,若是您不处理全部枚举值,您将收到警告:

 1 var aColor = Color.blue;
 2 
 3 switch (aColor) {
 4   case Color.red:
 5     print('Red as roses!');
 6     break;
 7   case Color.green:
 8     print('Green as grass!');
 9     break;
10   default: // Without this, you see a WARNING.
11     print(aColor); // 'Color.blue'
12 }

枚举类型具备如下限制:

  • 您不能子类化,混合或实现枚举。
  • 您没法显式实例化枚举。

有关更多信息,请参阅Dart语言规范

向类添加功能:mixins

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

要使用 mixin,请使用with关键字后跟一个或多个mixin名称。如下示例显示了两个使用mixins的类:

 1 class Musician extends Performer with Musical {
 2   // ···
 3 }
 4 
 5 class Maestro extends Person
 6     with Musical, Aggressive, Demented {
 7   Maestro(String maestroName) {
 8     name = maestroName;
 9     canConduct = true;
10   }
11 }

要实现 mixin,请建立一个扩展Object的类,而且不声明构造函数。除非您但愿mixin可用做常规类,不然请使用mixin关键字而不是class。例如:

 1 mixin Musical {
 2   bool canPlayPiano = false;
 3   bool canCompose = false;
 4   bool canConduct = false;
 5 
 6   void entertainMe() {
 7     if (canPlayPiano) {
 8       print('Playing piano');
 9     } else if (canConduct) {
10       print('Waving hands');
11     } else {
12       print('Humming to self');
13     }
14   }
15 }

要指定只有某些类型可使用mixin - 例如,因此你的mixin能够调用它没有定义的方法 - 用于on指定所需的超类:

1 mixin MusicalPerformer on Musician {
2   // ···
3 }

版本说明:mixin Dart 2.1中引入了对关键字的支持。一般使用早期版本中的代码abstract class。有关2.1 mixin更改的更多信息,请参阅 Dart SDK changelog2.1 mixin规范。

类变量和方法

使用static关键字实现类范围的变量和方法。

静态变量

静态变量(类变量)对于类范围的状态和常量颇有用:

1 class Queue {
2   static const initialCapacity = 16;
3   // ···
4 }
5 
6 void main() {
7   assert(Queue.initialCapacity == 16);
8 }

静态变量在使用以前不会初始化。

注意: 此页面遵循 首选的常量名称的样式指南建议lowerCamelCase

静态方法

静态方法(类方法)不对实例进行操做,所以无权访问this。例如:

 1 import 'dart:math';
 2 
 3 class Point {
 4   num x, y;
 5   Point(this.x, this.y);
 6 
 7   static num distanceBetween(Point a, Point b) {
 8     var dx = a.x - b.x;
 9     var dy = a.y - b.y;
10     return sqrt(dx * dx + dy * dy);
11   }
12 }
13 
14 void main() {
15   var a = Point(2, 2);
16   var b = Point(4, 4);
17   var distance = Point.distanceBetween(a, b);
18   assert(2.8 < distance && distance < 2.9);
19   print(distance);
20 }

注意: 对于经常使用或普遍使用的实用程序和功能,请考虑使用顶级函数而不是静态方法。

您可使用静态方法做为编译时常量。例如,您能够将静态方法做为参数传递给常量构造函数。

泛型

若是您查看基本数组类型的API文档 List,您会看到该类型其实是List<E>。<...>表示法将List标记为 通用(或参数化)类型 - 具备正式类型参数的类型。按照惯例,大多数类型变量都有单字母名称,例如E,T,S,K和V.

为何要使用泛型?

类型安全一般须要泛型,但它们比仅容许代码运行有更多好处:

  • 正确指定泛型类型会产生更好的生成代码。
  • 您可使用泛型来减小代码重复。

若是您但愿列表只包含字符串,则能够将其声明为List<String>(将其读做“字符串列表”)。这样,您,您的程序员和您的工具能够检测到将非字符串分配给列表多是一个错误。这是一个例子:

1 var names = List<String>();
2 names.addAll(['Seth', 'Kathy', 'Lars']);
3 names.add(42); // Error

使用泛型的另外一个缘由是减小代码重复。泛型容许您在多种类型之间共享单个接口和实现,同时仍然利用静态分析。例如,假设您建立了一个用于缓存对象的接口:

1 abstract class ObjectCache {
2   Object getByKey(String key);
3   void setByKey(String key, Object value);
4 }

您发现须要此接口的特定于字符串的版本,所以您须要建立另外一个接口:

1 abstract class StringCache {
2   String getByKey(String key);
3   void setByKey(String key, String value);
4 }

以后,您决定要使用此接口的数字版本...您明白了。

通用类型能够省去建立全部这些接口的麻烦。相反,您能够建立一个带有类型参数的接口:

1 abstract class Cache<T> {
2   T getByKey(String key);
3   void setByKey(String key, T value);
4 }

在此代码中,T是替身类型。它是一个占位符,您能够将其视为开发人员稍后定义的类型。

使用集合文字

能够参数化列表,集和Hash集合文字。参数化文字就像你已经看到的文字同样,除了你在开始括号以前添加 (对于列表和集合)或 (对于Hash集合)。如下是使用类型文字的示例:<type><keyType, valueType>

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

使用带有构造函数的参数化类型

要在使用构造函数时指定一个或多个类型,请将类型放在<...>类名称后面的尖括号()中。例如:

var nameSet = Set<String>.from(names);

如下代码建立一个具备整数键和View类型值的映射:

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

通用集合及其包含的类型

Dart泛型类型的具体化,这意味着他们随身携带的类型信息在运行时。例如,您能够测试集合的类型:

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

注意: 相反,Java中的泛型使用擦除,这意味着在运行时删除泛型类型参数。在Java中,您能够测试对象是否为List,但您没法测试它是否为a List<String>

限制参数化类型

实现泛型类型时,您可能但愿限制其参数的类型。你可使用extends

1 class Foo<T extends SomeBaseClass> {
2   // Implementation goes here...
3   String toString() => "Instance of 'Foo<$T>'";
4 }
5 
6 class Extender extends SomeBaseClass {...}

使用SomeBaseClass或其任何子类做为通用参数是能够的:

1 var someBaseClassFoo = Foo<SomeBaseClass>();
2 var extenderFoo = Foo<Extender>();

也能够不指定泛型参数:

1 var foo = Foo();
2 print(foo); // Instance of 'Foo<SomeBaseClass>'

指定任何非SomeBaseClass类型会致使错误:

var foo = Foo<Object>();

使用通用方法

最初,Dart的通用支持仅限于课程。一种称为泛型方法的新语法容许在方法和函数上使用类型参数:

1 T first<T>(List<T> ts) {
2   // Do some initial work or error checking, then...
3   T tmp = ts[0];
4   // Do some additional checking or processing...
5   return tmp;
6 }

这里on first<T>)的泛型类型参数容许你T在几个地方使用type参数:

  • 在函数的返回类型(T)中。
  • 在参数类型(List<T>)中。
  • 在局部变量的类型(T tmp)。

有关泛型的更多信息,请参阅 使用泛型方法。

Libraries和visibility

importlibrary指令能够帮助您建立一个模块化的,可共享的代码库。库不只提供API,仍是隐私单元:如下划线(_)开头的标识符仅在库内可见。每一个Dart应用程序都是一个库,即便它不使用library指令。

可使用包来分发库。 有关pub(包含在SDK中的包管理器)的信息,请参阅 Pub Package和Asset Manager

使用库

使用import指定如何从一个库中的命名空间在另外一个库的范围内使用。

例如,Dart Web应用程序一般使用dart:html 库,它们能够像这样导入:

import 'dart:html';

惟一须要的参数import是指定库的URI。对于内置库,URI具备特殊dart:方案。对于其余库,您可使用文件系统路径或package: 方案。该package:方案指定由包管理器(如pub工具)提供的库。例如:

import 'package:test/test.dart';

注意: URI表明统一资源标识符。 URL(统一资源定位符)是一种常见的URI。

指定库前缀

若是导入两个具备冲突标识符的库,则能够为一个或两个库指定前缀。例如,若是library1和library2都有一个Element类,那么你可能有这样的代码:

1 import 'package:lib1/lib1.dart';
2 import 'package:lib2/lib2.dart' as lib2;
3 
4 // Uses Element from lib1.
5 Element element1 = Element();
6 
7 // Uses Element from lib2.
8 lib2.Element element2 = lib2.Element();

仅导入库的一部分

若是只想使用库的一部分,则能够有选择地导入库。例如:

1 // Import only foo.
2 import 'package:lib1/lib1.dart' show foo;
3 
4 // Import all names EXCEPT foo.
5 import 'package:lib2/lib2.dart' hide foo;

懒惰地加载一个库

延迟加载(也称为延迟加载)容许应用程序根据须要加载库,若是须要的话。如下是您可能使用延迟加载的一些状况:

  • 减小应用程序的初始启动时间。
  • 例如,执行A / B测试 - 尝试算法的替代实现。
  • 加载不多使用的功能,例如可选的屏幕和对话框。

要懒洋洋地加载库,必须先使用它导入它deferred as

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

当您须要库时,loadLibrary()使用库的标识符进行调用 。

1 Future greet() async {
2   await hello.loadLibrary();
3   hello.printGreeting();
4 }

在前面的代码中,await关键字暂停执行,直到加载库。有关详细信息async,并await请参阅异步支持

您能够loadLibrary()在库上屡次调用而不会出现问题。该库只加载一次。

使用延迟加载时请记住如下内容:

  • 延迟库的常量不是导入文件中的常量。请记住,在加载延迟库以前,这些常量不存在。
  • 您不能在导入文件中使用延迟库中的类型。相反,请考虑将接口类型移动到由延迟库和导入文件导入的库。
  • Dart隐式插入loadLibrary()您使用的命名空间。该函数返回Futuredeferred asnamespaceloadLibrary()

Dart VM差别: 即便在调用以前,Dart VM也容许访问延迟库的成员loadLibrary()。此行为可能会更改,所以 不要依赖于当前的VM行为。 有关详细信息,请参阅问题#33118。

实现库

有关 如何实现库包的建议,请参阅 建立库包,包括:

  • 如何组织库源代码。
  • 如何使用该export指令。
  • 什么时候使用该part指令。
  • 什么时候使用该library指令。

 

异步支持

Dart库中包含许多返回FutureStream对象的函数。这些函数是异步的:它们在设置可能耗时的操做(例如I / O)后返回,而不等待该操做完成。

asyncawait关键字支持异步编程,让你写异步代码看起来相似于同步代码。

 

处理期货

当您须要完成Future的结果时,您有两个选择:

使用asyncawait异步的代码,但它看起来很像同步代码。例如,这里有一些代码await 用于等待异步函数的结果:

await lookUpVersion();

要使用await,代码必须在异步函数中 - 标记为的函数async

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

注意: 虽然异步函数可能会执行耗时的操做,但它不会等待这些操做。相反,异步函数只在遇到第一个await表达式(详细信息)时执行。而后它返回一个Future对象,仅在await表达式完成后才恢复执行。

使用trycatchfinally 处理使用await如下代码的错误和清理:

1 try {
2   version = await lookUpVersion();
3 } catch (e) {
4   // React to inability to look up the version
5 }

您能够await在异步功能中屡次使用。例如,如下代码等待三次函数结果:

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

在,价值一般是将来; 若是不是,那么该值将自动包装在Future中。此Future对象表示返回对象的承诺。值是返回的对象。await表达式使执行暂停,直到该对象可用。await expressionexpressionawait expression

若是在使用时出现编译时错误await,请确保await处于异步功能中。 例如,要await在您的应用程序的main()功能中使用,main()必须将正文标记为async

1 Future main() async {
2   checkVersion();
3   print('In main: version is ${await lookUpVersion()}');
4 }

声明异步函数

一个异步函数是一个函数体标有async修改。

async关键字添加到函数使其返回Future。例如,考虑这个同步函数,它返回一个String:

String lookUpVersion() => '1.0.0';

若是将其更改成异步函数 - 例如,由于未来的实现将很是耗时 - 返回的值是Future:

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

请注意,函数的主体不须要使用Future API。若有必要,Dart会建立Future对象。

若是您的函数没有返回有用的值,请设置其返回类型Future<void>

处理流

当您须要从Stream获取值时,您有两个选择:

  • 使用async和异步for循环(await for)。
  • 使用Stream API,如 库浏览中所述

注意: 在使用以前await for,请确保它使代码更清晰,而且您确实但愿等待全部流的结果。例如,你一般应该不使用await for的UI事件侦听器,由于UI框架发送事件的人流如潮。

异步for循环具备如下形式:

await for (varOrType identifier in expression) {
  // Executes each time the stream emits a value.
}

expression必须具备Stream类型。执行过程以下:

  1. 等到流发出一个值。
  2. 执行for循环的主体,将变量设置为该发出的值。
  3. 重复1和2,直到关闭流。

要中止侦听流,可使用breakor return语句,该for语句会从for循环中断开并从流中取消取消。

若是在实现异步for循环时遇到编译时错误,请确保await for它处于异步函数中。 例如,要在应用程序的main()函数中使用异步for循环,main()必须将正文标记为async

1 Future main() async {
2   // ...
3   await for (var request in requestServer) {
4     handleRequest(request);
5   }
6   // ...
7 }

有关异步编程的更多信息,请参阅 库浏览的 dart:async部分。另请参阅文章 Dart语言异步支持:阶段1 和Dart语言异步支持:阶段2Dart语言规范

Generators

当您须要懒惰地生成一系列值时,请考虑使用生成器函数。Dart内置支持两种Generators功能:

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

要实现同步生成器函数,请将函数体标记为sync*,并使用yield语句来传递值:

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

要实现异步生成器函数,请将函数体标记为async*,并使用yield语句来传递值:

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

若是您的生成器是递归的,您可使用yield*如下方法来提升其性能:

1 Iterable<int> naturalsDownFrom(int n) sync* {
2   if (n > 0) {
3     yield n;
4     yield* naturalsDownFrom(n - 1);
5   }
6 }

有关生成器的更多信息,请参阅文章 Dart语言异步支持:阶段2

可调用的类

要容许像函数同样调用Dart类,请实现该call()方法。

在下面的示例中,WannabeFunction该类定义了一个call()函数,它接受三个字符串并链接它们,用空格分隔每一个字符串,并附加一个感叹号。单击运行按钮以执行代码。

有关处理函数类的更多信息,请参阅 在Dart中模拟函数

分离

大多数计算机,即便在移动平台上,也有多核CPU。为了利用全部这些核心,开发人员传统上使用并发运行的共享内存线程。可是,共享状态并发容易出错,而且可能致使代码复杂化。

全部Dart代码都在隔离区内运行,而不是线程。每一个隔离区都有本身的内存堆,确保不会从任何其余隔离区访问隔离区的状态。

有关更多信息,请参阅 dart:isolate库文档。

类型定义

在Dart中,函数是对象,就像字符串同样,数字是对象。一个类型定义,或功能型的别名,给出了一个函数类型声明字段时,您可使用和返回类型的名称。当函数类型分配给变量时,typedef会保留类型信息。

请考虑如下代码,它不使用typedef:

 1 class SortedCollection {
 2   Function compare;
 3 
 4   SortedCollection(int f(Object a, Object b)) {
 5     compare = f;
 6   }
 7 }
 8 
 9 // Initial, broken implementation.
10 int sort(Object a, Object b) => 0;
11 
12 void main() {
13   SortedCollection coll = SortedCollection(sort);
14 
15   // All we know is that compare is a function,
16   // but what type of function?
17   assert(coll.compare is Function);
18 }

当分配类型信息丢失fcompare。类型 f(Object, Object)→ int(其中→表示返回),但类型compare是功能。若是咱们将代码更改成使用显式名称并保留类型信息,则开发人员和工具均可以使用该信息。

 1 typedef Compare = int Function(Object a, Object b);
 2 
 3 class SortedCollection {
 4   Compare compare;
 5 
 6   SortedCollection(this.compare);
 7 }
 8 
 9 // Initial, broken implementation.
10 int sort(Object a, Object b) => 0;
11 
12 void main() {
13   SortedCollection coll = SortedCollection(sort);
14   assert(coll.compare is Function);
15   assert(coll.compare is Compare);
16 }

注意: 目前,typedef仅限于函数类型。咱们但愿这会改变。

由于typedef只是别名,因此它们提供了一种检查任何函数类型的方法。例如:

1 typedef Compare<T> = int Function(T a, T b);
2 
3 int sort(int a, int b) => a - b;
4 
5 void main() {
6   assert(sort is Compare<int>); // True!
7 }

元数据

使用元数据提供有关代码的其余信息。元数据注释以字符开头@,后跟对编译时常量的引用(如deprecated)或对常量构造函数的调用。

全部Dart代码都有两个注释:@deprecated和 @override。有关使用的示例@override,请参阅扩展类。如下是使用@deprecated 注释的示例:

 1 class Television {
 2   /// _Deprecated: Use [turnOn] instead._
 3   @deprecated
 4   void activate() {
 5     turnOn();
 6   }
 7 
 8   /// Turns the TV's power on.
 9   void turnOn() {...}
10 }

您能够定义本身的元数据注释。这是一个定义带有两个参数的@todo注释的示例:

1 library todo;
2 
3 class Todo {
4   final String who;
5   final String what;
6 
7   const Todo(this.who, this.what);
8 }

如下是使用@todo注释的示例:

1 import 'todo.dart';
2 
3 @Todo('seth', 'make this do something')
4 void doSomething() {
5   print('do something');
6 }

元数据能够出如今库,类,typedef,类型参数,构造函数,工厂,函数,字段,参数或变量声明以前以及导入或导出指令以前。您可使用反射在运行时检索元数据。

注释

Dart支持单行注释,多行注释和文档注释。

单行注释

单行注释以//。开头。//Dart编译器会忽略行之间和行尾的全部内容。

1 void main() {
2   // TODO: refactor into an AbstractLlamaGreetingFactory?
3   print('Welcome to my Llama farm!');
4 }

多行注释

多行注释以... /*结尾*/。介于二者之间的/*,并*/用Dart编译器忽略(除非该注释是一个文档注释;见下一节)。多行注释能够嵌套。

 1 void main() {
 2   /*
 3    * This is a lot of work. Consider raising chickens.
 4 
 5   Llama larry = Llama();
 6   larry.feed();
 7   larry.exercise();
 8   larry.clean();
 9    */
10 }

文档注释

文档注释是首先多行或单行注释////**。使用///连续的行上有一个多行文档注释一样的效果。

在文档注释中,Dart编译器忽略全部文本,除非它括在括号中。使用括号,您能够引用类,方法,字段,顶级变量,函数和参数。括号中的名称在已记录的程序元素的词法范围内获得解析。

如下是文档注释的示例,其中引用了其余类和参数:

 1 /// A domesticated South American camelid (Lama glama).
 2 ///
 3 /// Andean cultures have used llamas as meat and pack
 4 /// animals since pre-Hispanic times.
 5 class Llama {
 6   String name;
 7 
 8   /// Feeds your llama [Food].
 9   ///
10   /// The typical llama eats one bale of hay per week.
11   void feed(Food food) {
12     // ...
13   }
14 
15   /// Exercises your llama with an [activity] for
16   /// [timeLimit] minutes.
17   void exercise(Activity activity, int timeLimit) {
18     // ...
19   }
20 }

在生成的文档中,[Food]成为Food类的API文档的连接。

要解析Dart代码并生成HTML文档,您可使用SDK的 文档生成工具。 有关生成的文档的示例,请参阅Dart API文档。有关如何构建注释的建议,请参阅 Dart Doc注释指南。

相关文章
相关标签/搜索