Dart 2官方文档中文版(Dart 编程语言导览)Part 1

导览

Dart编程语言导览

本文展示如何使用各个主要Dart功能,从变量、运算符到类和库,这里假定你已经至少了解如何使用一种其它编程语言。

要了解更多有关 Dart核心库的知识,请参见核心库导览。在想要了解更多有关语言功能的时候,请查询Dart语言规范

小贴士: 你可以借助DartPad来使用Dart语言的大部分功能 (了解更多)。

打开DartPad

一个Dart基础程序

以下代码使用了Dart的大量最基础的功能:

// 定义一个函数

printInteger(int  aNumber)  {

  print('The number is $aNumber.');  // 打印到控制台

}

// 这是应用开始执行的地方。

main()  {

  var  number  =  42;  // 声明和初始化一个变量

  printInteger(number);  // 调用函数

}

以下是这个程序所使用到的可用于所有(或几乎所有)的Dart应用的部分:

// 这是一条注释

单行注释。Dart还支持多行注释和文件注释。更多详情,请参见 注释.

int

一种类型。其它的一些 内置类型String, Listbool

42>

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

print()

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

'...' (or "...")

一个字符串字面量。

$*variableName* (或 ${*expression*})

插值字符串:包含等价于字符串字面量的变量或表达式的字符串。更多相关信息,请参见 字符串.

main()

在应用执行开始处的特殊的、必须的顶级函数。更多相关信息,请参见 main()函数.

var

一种无需指定类型声明变量的方式。

Note: 本站的代码遵循 Dart 样式指南中的准则。

重要概念

在学习Dart编程语言的过程中,请在心中保持以下事实和概念:

  • 在变量中放置的所有内容都是对象,并且每个对象都是一个类的实例。包括数字、函数和 null 都是对象。所有的对象都继承自 Object 类。
  • 虽然Dart是强类型语言,类型标注却是可选的,因为Dart可以推导类型。在以上的代码中, number 所推导的类型是 int。在你想要显式地说明无需设定类型, 使用特殊类型 dynamic.
  • Dart支持泛型,如 List<int> (整型列表) 或 List<dynamic> (任意类型对象的列表)。
  • Dart支持顶级函数(如 main()),以及与类或对象绑定的函数 (分别为静态和实例方法)。你也可以在函数内创建函数(嵌套或局部函数)。
  • 类型地,Dart支持顶级变量,以及与类或对象绑定的变量(静态和实例变量)。实例变量有时也称为字段或属性。
  • 不同于Java,Dart没有public, protectedprivate这些关键字。 如果一个标识符以下划线(_)开头,它是对库私有的。详情请参见 库和可见性.
  • 标识符可以字母或下划线 (_)开头,后接这些字符和数字的任意组合。
  • Dart既有表达式(拥有运行时值)也有语句(没有运行时值)。例如, 条件表达式 condition ? expr1 : expr2 有一个值expr1expr2。对比 if-else 语句则没有值。一条语句通常包含一个或多个表达式,但一个表达式不能直接包含一条语句。
  • Dart工具可以报出两类问题:警告(warning)和错误(error) 。警告只是表明你的代码可能无法运行,但并不会阻止程序的执行。错误可以是编译时或运行时的。编译时错误会完全阻止代码的执行,运行时错误会在代码执行时导致 异常 的抛出。

关键字

下表中列出了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版本之后添加的与异步支持相关的更新的、有限的保留词。你无法使用async, async*sync*标记的函数体内使用 awaityield 作为标识符。

表中的其它词都是保留词,无法用作标识符。

变量

以下是一个创建变量并初始化的示例:

var  name  =  'Bob';

变量存储引用。名为name的变量包含一个对值为”Bob”的String对象的引用。

变量name的类型推导为String,但你可以通过指定类型来进行修改。如果对象不仅限于单个类型,按照设计指南将其指定为Objectdynamic类型。

dynamic name  =  'Bob';

另一个选项是显式地将其声明为所要推导的类型:

String  name  =  'Bob';

Note: 本页中对局部变量按照 样式指南推荐 使用 var,而非类型注解。

默认值

未初始过的值有一个初始值null。即使是带有数值类型的变量初始值也是null,因为数值类型和Dart中的所有内容一样,都是对象。

int  lineCount;

assert(lineCount  ==  null);

Note: 生产代码会忽略 assert() 调用。而在开发期间,当 if 条件为假时 assert(*condition*) 会抛出一个异常。详情请参见 Assert

Final和const

如果你不想要修改一个变量,使用 finalconst来替代 var 或加前类型前。final变量只能设置一次,const变量是编译时常量。(const变量是隐式的final)。final顶级或类变量在初次使用时进行初始化。

Note: 实例变量可以是 final ,但不能为 const。final实例变量必须在构造函数体开始之前进行初始化,通过构造函数参数在变量声明中或在构造函数的初始化程序列表中。

以下是一个创建和设置final变量的示例:

final  name  =  'Bob';  // 无类型标注

final  String  nickname  =  'Bobby';

你无法修改final变量的值:


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

对你希望是编译时常量的变量使用const。如果const变量是类级别的,将其标记为 static const。在声明变量之处,设置值为编译时常量,如数值或字符串字面量、const变量或对常数的算术运算结果:

const  bar  =  1000000;  // 单位压强 (dynes/cm2)

const  double  atm  =  1.01325  *  bar;  // 标准大气

const 关键字不只是为声明常变量的。你还可以使用它来创建常量值,以及声明创建常量值的构造函数。任意变量可拥有常量值。

var  foo  =  const  [];

final  bar  =  const  [];

const  baz  =  [];  // 等价于 const []

你可以在const声明的初始化表达式中省略 const,像上面的 baz 。详情请参见 不要重复使用const.

你可以修改一个非final、非const变量的值,即使它曾经是一个const值:

foo  =  [1,  2,  3];  // 曾经是const []

但是不能修改const变量的值:


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

更多有关使用 const 创建常量值的内容,请参见 列表, 映射

内置类型

Dart语言拥有对如下类型的特别支持:

  • 数值
  • 字符串
  • 布尔型
  • 列表 (也称为数组)
  • 映射
  • rune (用于在字符串表示Unicode字符串)
  • 符号

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

因为Dart中的每个变量都引用一个对象 – 一个类的实例,通常可以使用构造函数来初始化变量。一些内置类型有它们自己的构造函数。例如,你可以使用 Map() 构造函数来创建一个映射。

数值

Dart有两种数值类型:

int

整型值根据平台不大于64位。在 Dart VM中,值可以为 -263 到 263 – 1。编译为JavaScript的Dart使用 JavaScript数值, 允许的值为 -253 到 253 – 1。

double

64位(双精度)浮点数值,如IEEE 754 标准中所描述。

intdoublenum的子类型。num类型包含基本运算符如 +, -, / 和 *,也包含其它方法中的 abs(),ceil()floor()。(位相关的运算符,如 >>,在 int 类中定义)。 如果num及其子类型中没有你所要寻找的,可以使用 dart:math 库。

整型是不包含小数点的数字。以下是一些定义整型字面量的示例:

var  x  =  1;

var  hex  =  0xDEADBEEF;

如果数值中包含小数点,则为double类型。以下是一些定义double字面量的示例:


var  y  =  1.1;

var  exponents  =  1.42e5;

在 Dart 2.1中,整型字面在需要时会自动转化为double值:

double  z  =  1;  // 等价于 z = 1.0.

Version note: 在Dart 2.1之前,在double上下文中使用整型字面量会报错。

以下是如何将字符串转化为数字及其反向操作:

// String -> int

var  one  =  int.parse('1');

assert(one  ==  1);

// String -> double

var  onePointOne  =  double.parse('1.1');

assert(onePointOne  ==  1.1);

// int -> String

String  oneAsString  =  1.toString();

assert(oneAsString  ==  '1');

// double -> String

String  piAsString  =  3.14159.toStringAsFixed(2);

assert(piAsString  ==  '3.14');

int类型指定了传统的按钮移动 (<<, >>)、与 (&)和 或 (|)运算符。例如:


assert((3  <<  1)  ==  6);  // 0011 << 1 == 0110

assert((3  >>  1)  ==  1);  // 0011 >> 1 == 0001

assert((3  |  4)  ==  7);  // 0011 | 0100 == 0111

字面量数字是编译时常量。很多算术表达式也是编译时常量,只要它们的运算项是运算为数值的编译时常量。

const  msPerSecond  =  1000;

const  secondsUntilRetry  =  5;

const  msUntilRetry  =  secondsUntilRetry *  msPerSecond;

字符串

Dart字符串是一个UTF-16代码单元。你可以使用单引号或双引号来创建字符串:

var  s1  =  'Single quotes work well for string literals.';

var  s2  =  "Double quotes work just as well.";

var  s3  =  'It\'s easy to escape the string delimiter.';

var  s4  =  "It's even easier to use the other delimiter.";

可以通过使用${expression}来将表达式的值放到字符串中。如果表达式是一个标识符,可以省略{}。要获取一个对象对应的字符串,Dart中调用对象的toString()方法。

var  s  =  'string interpolation';

assert('Dart has $s, which is very handy.'  ==

    'Dart has string interpolation, '  +

        'which is very handy.');

assert('That deserves all caps. '  +

        '${s.toUpperCase()} is very handy!'  ==

    'That deserves all caps. '  +

        'STRING INTERPOLATION is very handy!');

Note: == 运算符用于测试两个对象是否相等。如果两个字符串拥有相同序列的代码单元则相等。

可以使用相邻字符串字面量或者了 + 运算符来拼接字符串:

var  s1  =  'String '

    'concatenation'

    " works even over line breaks.";

assert(s1  ==

    'String concatenation works even over '

        'line breaks.');

var  s2  =  'The + operator '  +  'works, as well.';

assert(s2  ==  'The + operator works, as well.');

另一种创建多行字符串的方式是使用三个单引号或三个双引号标记:


var  s1  =  '''

You can create

multi-line strings like this one.

''';

var  s2  =  """This is also a

multi-line string.""";

可以通过r前缀来创建“原生”字符串:

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

参见 Rune 来获取如何在字符串中表达Unicode字符的详情。

字符串字面量是编译时常量,仅需编译时常量中的插值表达式运行结果为null或数值、字符串或布尔值。

// 这些可以在一个const字符串中使用

const  aConstNum  =  0;

const  aConstBool  =  true;

const  aConstString  =  'a constant string';

// 这些不可以在一个const字符串中使用

var  aNum  =  0;

var  aBool  =  true;

var  aString  =  'a string';

const  aConstList  =  [1,  2,  3];

const  validConstString  =  '$aConstNum $aConstBool $aConstString';

// const invalidConstString = '$aNum $aBool $aString $aConstList';

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

布尔型

要表现布尔值,Dart中有一个名为bool的类型。仅有两个对象的类型为bool:布尔型字面量truefalse,它们都是编译时常量。

Dart的类型安全意味着你不能使用if (*nonbooleanValue*)assert (*nonbooleanValue*)这样的代码。而是要显式的检查值,类似这样:

// 检查空字符串

var  fullName  =  '';

assert(fullName.isEmpty);

// 检查0值

var  hitPoints  =  0;

assert(hitPoints  <=  0);

// 检查null值

var  unicorn;

assert(unicorn  ==  null);

// 检查NaN

var  iMeantToDoThis  =  0  /  0;

assert(iMeantToDoThis.isNaN);

列表

或许在几乎所有编程语言中最常见的集合(collection)都是数组,或有序的对象组。在Dart,数组是List对象,因此大部分会称其为列表。

Dart的列表字面量和JavaScript中的数组字面量很像。以下是一个简单的Dart列表:

var  list  =  [1,  2,  3];

Note: Dart推导 list 的类型为 List<int>。如果你尝试向这个列表中添加非整型对象,分析器或运行时会抛出错误。更多信息请阅读 类型推导

列表使用基于0的索引,即0是第1个元素的索引,并且 list.length - 1 是最后一个元素的索引。你可以完全像JavaScript中那样获取一个列表的长度并引用列表元素:

var  list  =  [1,  2,  3];

assert(list.length  ==  3);

assert(list[1]  ==  2);

list[1]  =  1;

assert(list[1]  ==  1);

要创建一个为运行是常量的列表,在列表字面量之前添加 const


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

// constantList[1] = 1; // 取消这行的注释会导致报错

Dart 2.3 引入了展开运算符(...) 及判空展开运算符 (...?),提供一种向集合插入多个元素的简洁方式。

例如,你可以使用展开运算符 (...) 来将列表中的所有元素插入到另一个列表中:


var  list  =  [1,  2,  3];

var  list2  =  [0,  ...list];

assert(list2.length  ==  4);

如果展开运算符右侧的表达式有可能为null,可通过使用可判空展开运算符(...?)来避免异常:

var  list;

var  list2  =  [0,  ...?list];

assert(list2.length  ==  1);

更多有关使用展开运算符的详情和示例,参见展开运算符提议

Dart 2.3 还引入了collection ifcollection for,可以使用条件(if)和循环 (for)来构建使用条件(if)和循环 (for)的集合。

以下是使用collection if来创建一个包含3项或4项的列表::

var  nav  =  [

  'Home',

  'Furniture',

  'Plants',

  if  (promoActive)  'Outlet'

];

以下是使用collection for来在将它们添加到另一个列表之前操作列表项的示例:


var  listOfInts  =  [1,  2,  3];

var  listOfStrings  =  [

  '#0',

  for  (var  i  in  listOfInts)  '#$i'

];

assert(listOfStrings[1]  ==  '#1');

更多有关使用collection if和for的示例,参见 控制流collection建议

列表类型有很多操作列表的便捷的方法。有关列表更多的信息,请参见泛型集合.

Dart中的集(set)是一个包含独立项的无序集合。Dart对集的支持由集字面量和Set类型所提供。

Version note: 虽然Set类型一直是Dart的核心部分,集字面量在Dart 2.2中才引入。

以下是一个简单的Dart集,使用集字面量创建:


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

Note: Dart推导 halogens 的类型为 Set<String>。 如果你尝试向集添加错误类型的值,分析器或运行时会抛出错误。更多信息,请阅读类型推导部分。

要创建空集,使用带有类型前缀的{}或向类型为Set的变量赋值{}


var  names  =  <String>{};

// Set<String> names = {}; // 这也同样起作用

// var names = {}; // 创建一个映射,而非集

集或映射? 映射字面量的语法类型集字面量。因为映射字面量的优先级更高, {} 的默认为 Map 类型。如果你忘记对 {} 进行类型标注,或者它所赋值的变量,那么Dart会创建一个类型为 Map<dynamic, dynamic>的对象。

使用add()addAll() 方法向已有集添加子项:


var  elements  =  <String>{};

elements.add('fluorine');

elements.addAll(halogens);

使用 .length 来获取集中子项的数量:


var  elements  =  <String>{};

elements.add('fluorine');

elements.addAll(halogens);

assert(elements.length  ==  5);

创建为运行时常量的集,在集字面量前添加const


final  constantSet  =  const  {

  'fluorine',

  'chlorine',

  'bromine',

  'iodine',

  'astatine',

};

// constantSet.add('helium'); // 取消本行注释会导致报错

在Dart 2.3中,集也像列表一样支持展开运算符 (......?) 以及 collection if 和 for。更多信息,参见 列表展开运算符列表集合运算符 部分的讨论。

有关集的更多知识,请参见 泛型.

映射

总的来说,映射是一个关联键和值的对象。键和值都可以为任意对象类型。每个键仅出现一次,但相同值可出现多次。Dart对映射的支持由映射字面量和 Map 类型所提供。

以下是一些简单的Dart映射,使用映射字面量创建:

var  gifts  =  {

  // Key:    Value

  'first':  'partridge',

  'second':  'turtledoves',

  'fifth':  'golden rings'

};

var  nobleGases  =  {

  2:  'helium',

  10:  'neon',

  18:  'argon',

};

Note: Dart推导 gifts 的类型为 Map<String, String> ,而nobleGases 的类型为 Map<int, String>。如果你尝试对任一映射添加错误类型的值,分析器或运行时会抛出错误。更多相关信息,请参阅 类型推导

可以使用Map构造函数创建相同的对象:

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

Note: 你可能会希望看到 new Map() 而非仅仅是 Map()。从Dart 2中,关键字 new 为可选部分。更多详情,参见使用构造函数

和JavaScript中一样向已有映射添加键值对:

var  gifts  =  {'first':  'partridge'};

gifts['fourth']  =  'calling birds';  // 添加一个键值对

以JavaScript同样的方式从映射中接收值:

var  gifts  =  {'first':  'partridge'};

assert(gifts['first']  ==  'partridge');

如果你在寻找一个不在映射中的键,会在返回中获取到null:

var  gifts  =  {'first':  'partridge'};

assert(gifts['fifth']  ==  null);

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

var  gifts  =  {'first':  'partridge'};

gifts['fourth']  =  'calling birds';

assert(gifts.length  ==  2);

创建为编译时常量的映射,在映射字面量之前添加const


final  constantMap  =  const  {

  2:  'helium',

  10:  'neon',

  18:  'argon',

};

// constantMap[2] = 'Helium'; // 取消本行注释会导致报错

在Dart 2.3中,映射像列表一样支持展开运算符 (......?) 及collection if 和 for。有关详情及示例,参见 展开运算符提议控制流集合提议

更多有关映射的信息,参见 泛型映射.

Runes

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

Unicode为全球书写系统中的每个字母、数字和符号定义了一个独立的数值。因为Dart字符串是一个UTF-16代码单元的序列,在字符串中表达32位Unicode值要求有特殊的语法。

通常表达一个Unicode代码点的方式为 \uXXXX,其中XXXX是一个4位数的16进制值。例如,心形字符串 (♥) 为 \u2665。要指定比4位16进制更多或更少的位数,需将值放在花括号中。例如,笑脸emoji (😆) 为 \u{1f600}

String 类有多个可用于提取rune信息的属性。 codeUnitAtcodeUnit属性返回16-位代码单元。使用 runes 属性来获取字符串的rune。

以下示例描述runes、16-位代码单元以及32-位代码单元之间的关系。点击 Run 来实时查看rune。

Screen Shot 2019-08-28 at 6.48.25 PM.png

Note: 在使用列表运算符操作runes时要小心。这种方法根据具体语言、字符集和操作可能会很容易崩溃。更多相关信息,参见Stack Overflow上的 如何倒排Dart中的字符串?

符号

符号(Symbol) 对象表示在Dart程序中声明的一个运算符或标识符。你可能永远都不会需要用到符号,但它们对于通过名称引用标识符的 API 有无限的价值,因为最小化会修改标识符的名称但不修改标识符符号。

使用符号字面量获取一个标识符的符号,它只是#后接标识符。

#radix

#bar<code class="language-nocode">

符号字面量是编译时常量。

函数

Dart是一个真面向对象语言,因此即使是函数也是对象,并且拥有一个类型Function。这表示函数可赋值给变量或作为参数传递给其它函数。也可以调用将Dart类的实例看作函数来进行调用。更多详情,参见 可调用类

以下是实现一个函数的示例:

bool  isNoble(int  atomicNumber)  {

  return  _nobleGases[atomicNumber]  !=  null;

}

虽然高效Dart推荐 为公有 API 进行类型标注,函数则省略类型时依然有效:


isNoble(atomicNumber)  {

  return  _nobleGases[atomicNumber]  !=  null;

}

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

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

=> *expr* 语法是{ return *expr*; }的简写。 => 标记有时被称为箭头语法。

Note: 仅有一个表达式 – 而不是一条语句 – 可以出现在箭头(=>)和分号(;)之间。例如,你不能在这里放置一条 if语句条件表达式

函数可以有两种类型的参数:必选和可选。必选参数放在前面,后接可选参数。可选参数可为命名参数或位置参数。

Note: 有些API – 尤其是Flutter widget构造函数 – 即使用对改造参数也仅使用命名参数。参见下面一节了解更多详情。

可选参数

可选参数可以为命名参数或位置参数,或者是两者都有。

命名参数

在调用一个函数时,你可以使用*paramName*: *value*指定命名参数。例如:

enableFlags(bold:  true,  hidden:  false);

在定义函数时,使用 {*param1*, *param2*, …}来指定命名参数:


/// 设置bold 和 hidden标记 ...

void  enableFlags({bool  bold,  bool  hidden})  {...}

虽然命名参数是一种可选参数,通过可以@required 来标记它们以表示该参数是必须 – 即用户必须为该参数提供一个值。例如:

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

如果有人尝试不指定child参数就创建一个Scrollbar ,那么分析器会报出问题。

使用@required 标注,需要依赖meta 包并导入 package:meta/meta.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');

以下是使用了第3个参数调用该函数的示例:

assert(say('Bob',  'Howdy',  'smoke signal')  ==

    'Bob says Howdy with a smoke signal');

默认参数值

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

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

/// 设置bold 和 hidden标记 ...

void  enableFlags({bool  bold  =  false,  bool  hidden  =  false})  {...}

// bold将为true; hidden将为false

enableFlags(bold:  true);

Deprecation note: 老代码中可能会使用冒号 (:) 代替 = 来设置命名函数的默认值。原因是原来命名函数中仅支持 : 。该支持可能会被淘汰,所以我们推荐使 用= 来指定默认值。

下面的示例展示如何为位置参数设置默认值:

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

也可以传递列表或映射作为默认值。以下示例定义了一个函数doStuff(),它为 list参数指定了一个默认列表并为 gifts 参数指定了一个默认映射。

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()函数

每个应用必须有一个顶级的 main() 函数,它作为应用的入口。 main() 函数返回 void 并有一个可选参数l List<String>

以下是一个针对web应用的 main() 函数示例:

void  main()  {

  querySelector('#sample_text_id')

    ..text  =  'Click me!'

    ..onClick.listen(reverseText);

}

Note: 以下代码中 .. 语法称作一个 级联。通过级联,你可以对单个对象的成员执行多个操作。

以下是一个针对接收参数的命令行应用的main() 函数示例:

// 像这样运行它: 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库 来定义和解析命令行参数。

函数作为一等对象

你可以传递函数来作为另一个函数的参数。例如:

void  printElement(int  element)  {

  print(element);

}

var  list  =  [1,  2,  3];

// 传递 printElement作为参数

list.forEach(printElement);

可将函数赋值给变量,如:

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

assert(loudify('hello')  ==  '!!! HELLO !!!');

该例使用了匿名函数,更多相关内容请见下一节。

匿名函数

大部分函数都是命名函数,如 main()printElement()。你也可创建一个无名称函数,称为匿名函数,有时也称为lambda 或闭包。可以将匿名函数赋值给变量,那么例如就可以从集合中添加或删除它。

匿名函数类似于命名函数 – 0或多个参数,在括号中由逗号和可选选类型标注分隔。

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

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

以下示例定义了一个带有无类型参数 item 的匿名函数。对列表中的第一项所调用的函数,打印包含在指定索引处的值的字符串。

var  list  =  ['apples',  'bananas',  'oranges'];

list.forEach((item)  {

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

});

点击 Run 执行下面的代码。

点击查看

如果函数仅包含一条语句,可将其简化为使用箭头标记。将如下行拷贝到DartPad中并点击Run来验证它的功能是相同的。

list.forEach(

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

词法作用域

Dart是一个词法作用域语言,表示变量的作用域仅由代码的布局静态决定。你可以“按照外部花括号” 来查看变量是否在作用域中。

以下是一个变量在各自作用域中嵌套函数的示例:

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() 是如何使用每个级别的变量的,一路到顶级的变量。

词法闭包

闭包是一个即使函数位置原作用域之外也可在词法作用域中访问变量的函数对象。

函数可封闭周围作用域中所定义的变量。在以下示例中, makeAdder() 捕获变量 addBy。不论返回的函数放在哪里,它都能记住 addBy

/// 返回一个将addBy添加到函数参数中的函数

Function  makeAdder(num addBy)  {

  return  (num  i)  =>  addBy  +  i;

}

void  main()  {

  // 创建一个加2的函数

  var  add2  =  makeAdder(2);

  // 创建一个加4的函数

  var  add4  =  makeAdder(4);

  assert(add2(3)  ==  5);

  assert(add4(3)  ==  7);

}

测试函数是否相等

下面是一个测试顶级函数、静态方法和实例方法是否相等的示例:

void  foo()  {}  // 一个顶级函数

class  A  {

  static  void  bar()  {}  // 一个静态方法

  void  baz()  {}  // 一个实例方法

}

void  main()  {

  var  x;

  // 对比顶级函数

  x  =  foo;

  assert(foo  ==  x);

  // 对比静态方法

  x  =  A.bar;

  assert(A.bar  ==  x);

  // 对比实例方法

  var  v  =  A();  // A 的实例 #1

  var  w  =  A();  // A 的实例 #2

  var  y  =  w;

  x  =  w.baz;

  // 这些闭包引用相同的实例 (#2),

  // 因此它们相等。

  assert(y.baz  ==  x);

  // 这些闭包引用不同的实例,

  // 因此它们不相等。

  assert(v.baz  !=  w.baz);

}

返回值

所有的函数都会返回值。如果未指定返回值,语句 return null; 会隐式地附加到函数体中。

foo()  {}

assert(foo()  ==  null);

运算符

Dart定义了下表中显示的运算符。你可以重载很多运算符,在重载运算符中进行了描述。

描述 运算符
一元后置 *expr*++ *expr*-- () [] . ?.
一元前置 -*expr* !*expr* ~*expr* ++*expr* --*expr*
乘除 * / % ~/
加减 + -
按位移 << >> >>>
按位与 &
按位异或 ^
按位或 ` `
关系和类型测试 >= > <= < as is is!
等于 == !=
逻辑与 &&
逻辑或 ` `
判空(null) ??
条件 *expr1* ? *expr2* : *expr3*
级联 ..
赋值 = *= /= += -= &= ^= etc.

Warning: 运算符优先级是对Dart解析器行为的估计。要获取确定性的答案,参见Dart语言规范中的语法。

在使用运算符时,可创建表达式。以下是一些运算符表达式的示例:

a++

a  +  b

a  =  b

a  ==  b

c  ?  a  :  b

a  is  T

运算符表格中, 每个运算符都比下面一行的运算符优先级要高。比如乘法运算符 % 的优先级高于(因此先执行)等号运算符 ==,而它的优先级又高于逻辑与运算符 &&。这个优先级表示以下两行代码的执行方式相同:

// 括号提升可读性

if  ((n  %  i  ==  0)  &&  (d  %  i  ==  0))  ...

// 更验阅读,但是等价的

if  (n  %  i  ==  0  &&  d  %  i  ==  0)  ...

Warning: 对于使用两个运算项的运算符,最左侧的运算项决定使用哪个版本的运算符。例如,如果有一个Vector对象和一个Point对象, aVector + aPoint 使用Vector版本的+。

算术运算符

Dart支持常见算术运算符,如下表所示:

运算符 含义
+
-*expr* 一元减,也称为否定(反转表达式的符号)
*
/
~/ 整除,返回一个整数结果
% 获取整数相除的余数(模)

示例:

assert(2  +  3  ==  5);

assert(2  -  3  ==  -1);

assert(2  *  3  ==  6);

assert(5  /  2  ==  2.5);  // 结果是双精度类型

assert(5  ~/  2  ==  2);  // 结果是整型

assert(5  %  2  ==  1);  // 余数

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*)

示例:

var  a,  b;

a  =  0;

b  =  ++a;  // 在b获取值之前对a递增

assert(a  ==  b);  // 1 == 1

a  =  0;

b  =  a++;  // 在b获取值之后对a递增

assert(a  !=  b);  // 1 != 0

a  =  0;

b  =  --a;  // 在b获取值之前对a递减

assert(a  ==  b);  // -1 == -1

a  =  0;

b  =  a--;  // 在b获取值之后对a递减

assert(a  !=  b);  // -1 != 0

比较和关系运算符

下表中列出了比较和关系运算符的含义。

运算符 含义
== 等于,参见下面的讨论
!= 不等于
> 大于
< 小于
>= 大于等于
<= 小于等于

测试两个对象x 和 y 是否表示相同的内容,使用 == 运算符。(在极少的情况中,需要知道两个对象是否是完全相同的对象,转而使用 identical()函数。) 以下展示 == 运算符如何运行:

  1. 如果xy 为null,如果两者都为null则返回true,如果只一个为null则返回false。
  2. 返回*x*.==(*y*)方法运行的结果 。(没错,==这样的运算符是对它们的第一项所调用的方法。你甚至可以重载很多运算符,包括 ==, 可参见 重载运算符。)

以下是使用每个比较和关系运算符的示例:

assert(2  ==  2);

assert(2  !=  3);

assert(3  >  2);

assert(2  <  3);

assert(3  >=  3);

assert(2  <=  3);

类型测试运算符

as, isis! 运算符对于在运行时检查类型非常方便。

运算符 含义
as 对象类型转换 (也用于指定 库前缀)
is 如果对象有指定的类型时为true
is! 如果对象有指定的类型时为false

如果obj实现了T所指定的接口的话则 obj is T 的结果为 true。例如,obj is Object 永远为true。

使用 as 运算符将对象转换为指定类型。通常,应使用它作为对象is测试的简写,后接使用该对象的表达式。例如,考虑使用如下代码:


if  (emp is  Person)  {

  // 类型检查

  emp.firstName  =  'Bob';

}

可以使用 as 运算符让代码更精简:


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

Note: 两种代码并不等同。如果 emp 为null 或者不是Person,第1个例子(带is)不会做任何事,第二个(带as)抛出异常。

赋值运算符

如你所见,可以使用=运算符进行赋值。仅为值为null的变量赋值时,使用 ??= 运算符。

// 为a赋值

a  =  value;

// 若b为null时为 b 赋值,否则,b 保持不变

b  ??=  value;

复合赋值运算符如 += 将运算和赋值合并在一起。

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

以下是复合赋值运算符运行的方式:

复合赋值 比较表达式
对于运算符 op a *op*= b a = a *op* b
示例: a += b a = a + b

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


var  a  =  2;  // 使用 =赋值

a *=  3;  // 赋值和乘法: a = a * 3

assert(a  ==  6);

逻辑运算符

可以使用逻辑运算符取反或合并布尔表达式。

运算符 含义
!*expr* 对紧接着的表达式进行取反(修改false 为 true,反之亦然)
` ` 逻辑或
&& 逻辑与

以下是一个使用逻辑运算符的示例:

if  (!done  &&  (col  ==  0  ||  col  ==  3))  {

  // ...Do something...

}

按位及左移右移运算符

可以在Dart中操作数字的单个位。通常,会对整型使用这些按位和移动运算符。

运算符 含义
&
` `
^ 异或
~*expr* 一元按位取反(0变成1,1变成0)
<< 左移
>> 右移

以下是使用按位和移动运算符的示例:

final  value  =  0x22;

final  bitmask  =  0x0f;

assert((value  &  bitmask)  ==  0x02);  // 与

assert((value  &  ~bitmask)  ==  0x20);  // 与 取反

assert((value  |  bitmask)  ==  0x2f);  // 或

assert((value  ^  bitmask)  ==  0x2d);  // 异或

assert((value  <<  4)  ==  0x220);  // 左移

assert((value  >>  4)  ==  0x02);  // 右移

条件表达式

Dart有两个运算符可以让我们简洁地运行可能会要求使用 if-else 语句的表达式:

*condition* ? *expr1* : *expr2*

若if条件为true, 运行 expr1 (并返回它的值),否则运行并返回 expr2的值。

*expr1* ?? *expr2*

expr1 为非空,返回其值,否则运行并返回 expr2的值。

在需要根据布尔表达式进行赋值时,考虑使用 ?:

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

如果要测试布尔表达式是否为null,考虑使用 ??

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

前例至少可通过另外两种方式进行编写,但都没有它简洁:

// 使用?: 运算符的稍长版本

String  playerName(String  name)  =>  name  !=  null  ?  name  :  'Guest';

// 使用if-else语句的非常长的版本

String  playerName(String  name)  {

  if  (name  !=  null)  {

    return  name;

  }  else  {

    return  'Guest';

  }

}

级联标记 (..)

级联 (..) 允许我们对同一对象进行一系列操作。除调用函数外,你还可以对相同的对象访问字段。这通常可节省创建临时变量的步骤并让我们能编写列流畅的代码。

思考如下代码:

querySelector('#confirm')  // 获取一个对象

  ..text  =  'Confirm'  // 使用它的成员

  ..classes.add('important')

  ..onClick.listen((e)  =>  window.alert('Confirmed!'));

第1个方法调用querySelector(),返回一个选择器对象。紧接级联标记的代码对选择器对象进行操作,它忽略所有后续可能返回的值。

前例等同于:T

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

对于返回实际对象的函数构建级联时要非常小心。例如,如下代码会失败:


var  sb  =  StringBuffer();

sb.write('foo')

  ..write('bar');  // Error: method 'write' isn't defined for 'void'.

sb.write() 调用返回void,而无法对 void构建级联。

Note: 严格的说,对级联使用的“双点号”标记并非运算符。它只是Dart语法的一部分。

其它运算符

我们已经在其它示例中看到过大部分剩余的运算符:

运算符 名称 含义
() 函数应用 表示一个函数调用
[] 列表访问 引用列表中所指定索引处的值
. 成员访问 引用一个表达式的属性;例如:foo.bar 从表达式foo中选择属性 bar
?. 条件成员访问 类似 ., 但最左侧的运算项可以为null;例如: foo?.barfoo中选择属性 bar ,除非 foo 为 null (这时 foo?.bar 的值为 null)

更多有关., ?... 运算符的信息,参见

流程控制语句

可以使用以下方式来控制Dart 代码中的流程

  • ifelse
  • for 循环
  • whiledowhile 循环
  • breakcontinue
  • switchcase
  • assert

也可以使用try-catchthrow 来影响控制流程,在 异常中有进行讲解。

if和else

Dart 支持带有可选的else语句的 if语句,如下例中所示。同时请参见 条件表达式

if  (isRaining())  {

  you.bringRainCoat();

}  else  if  (isSnowing())  {

  you.wearJacket();

}  else  {

  car.putTopDown();

}

不同于JavaScript,条件必须使用布尔值,其它的都不行。参见 布尔型 获取更多信息。

for循环

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

var  message  =  StringBuffer('Dart is fun');

for  (var  i  =  0;  i  <  5;  i++)  {

  message.write('!');

}

Dart for 循环中的装饰捕获索引的值,避免JavaScript中所发现的常见问题。例如,思考:

var  callbacks  =  [];

for  (var  i  =  0;  i  <  2;  i++)  {

  callbacks.add(()  =>  print(i));

}

callbacks.forEach((c)  =>  c());

会如所预期的先输出 0 再输出 1。但对比在JavaScript中则会先打印 2 再打印 2

如果所迭代的对象是一个 Iterable,可以使用forEach() 方法。如果无需知道当前的迭代计数器的话使用 forEach() 是一个很好的选择:

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

Iterable类如List 和 Set 还把持迭代for-in 形式:

var  collection  =  [0,  1,  2];

for  (var  x  in  collection)  {

  print(x);  // 0 1 2

}

while和do-while

while 循环在循环之前运行条件:

while  (!isDone())  {

  doSomething();

}

dowhile 在循环之后运行条件:

do  {

  printLine();

}  while  (!atEndOfPage());

break和continue

使用 break 来停止循环:


while  (true)  {

  if  (shutDownRequested())  break;

  processIncomingRequests();

}

使用 continue 来跳至下一次迭代:

for  (int  i  =  0;  i  <  candidates.length;  i++)  {

  var  candidate  =  candidates[i];

  if  (candidate.yearsExperience  <  5)  {

    continue;

  }

  candidate.interview();

}

如果在使用列表或集这样的Iterable时可以会以不同的方式编写该示例:

candidates

    .where((c)  =>  c.yearsExperience  >=  5)

    .forEach((c)  =>  c.interview());

switch和case

Dart中的switch 语句使用==比较整型、字符串或编译时常量。 比较的对象必须都是相同类的实例(而非其子类型的),并且该类不能重载 ==.。枚举类型switch 语句中可以良好运行。

Note: Dart中的switch语句针对受限的环境中,如解释器或扫描器。

每个非空 case 从句按照规则以break语句结束。其它结束非空 case 从名的有效方式有 continue, throwreturn 语句。

在没有匹配的case从句时使用 default 从句来执行代码:


var  command  =  'OPEN';

switch  (command)  {

  case  'CLOSED':

    executeClosed();

    break;

  case  'PENDING':

    executePending();

    break;

  case  'APPROVED':

    executeApproved();

    break;

  case  'DENIED':

    executeDenied();

    break;

  case  'OPEN':

    executeOpen();

    break;

  default:

    executeUnknown();

}

下例在一个case从句中省略了 break 语句,因此产生了一个报错:

var  command  =  'OPEN';

switch  (command)  {

  case  'OPEN':

    executeOpen();

    // ERROR: Missing break

  case  'CLOSED':

    executeClosed();

    break;

}

但是,Dart确实支持空的 case 从句允许越过的形式:

var  command  =  'CLOSED';

switch  (command)  {

  case  'CLOSED':  // 空的case直接越过

  case  'NOW_CLOSED':

    // 同时对CLOSED 和 NOW_CLOSED运行

    executeNowClosed();

    break;

}

如果你真的希望越过,可以使用continue 语句及一个标签:


var  command  =  'CLOSED';

switch  (command)  {

  case  'CLOSED':

    executeClosed();

    continue  nowClosed;

  // 继续在nowClosed标签处执行

  nowClosed:

  case  'NOW_CLOSED':

    // 对CLOSED 和 NOW_CLOSED同时运行

    executeNowClosed();

    break;

}

case 从句可以有局部变量,仅在从句作用域内部可见。

断言

在开发过程中,可以使用断言语句 assert(*condition*, *optionalMessage*); 来在布尔条件为false时打断正常的执行。可以通过本导览找到断言语句的很多示例。以下是另外一些示例:


// 确保变量值为非null

assert(text  !=  null);

// 确保值小于100

assert(number  <  100);

// 确保这是一个https链接

assert(urlString.startsWith('https'));

要为断言关联消息,对assert添加一个字符串作为第2个参数。

assert(urlString.startsWith('https'),

    'URL ($urlString) should start with "https".');

assert 的第1个参数可以是解析为布尔值的任意表达式。如果表达式的值为true,断言成功且执行继续。如果它为false,断言失败且会抛出异常(一个AssertionError)。

断言到底做了什么呢?这取决于你所使用的工具和框架:

  • Flutter在调试模式中启用断言。
  • 仅针对开发的工具如 dartdevc 通常默认启用断言。
  • 一些工具,如 dartdart2js,支持通过命令行标记的断言: --enable-asserts.

在生产模式下,会忽略断言, assert 的参数不会被执行。

异常

Dart代码可以抛出和捕获异常。异常是表示预期外的事情发生时的报错。如未捕获到异常,抛出异常的 隔离(isolate)被挂起,并且通常隔离及其程序会被终止。

不同睛Java,Dart的异常都是非检查型异常。方法未声明它们所要抛出的异常,那么也不要求你捕获任何异常。

Dart提供 ExceptionError 类型,以及很多预定义的子类型。当然也可以定义自己的异常。但是Dart程序可抛出任意非空对象(不只是Exception和Error对象)来作为异常。

Throw

以下是一个抛出异常的示例:

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

你也可以抛出自己的对象:


throw  'Out of llamas!';

Note: 生产质量的代码通常抛出实现ErrorException的类型。

因为抛出异常是一个表达式,你可以在=>及其它允许使用表达式的任何地方抛出异常:

try  {

  breedMoreLlamas();

}  on  OutOfLlamasException  {

  buyMoreLlamas();

}

处理会抛出一个类型以上异常的代码,可以指定多个捕获分分钟。第一个匹配所抛出对象类型的catch从句会处理该异常。如果catch从句未指定类型,该分分钟可处理任意类型抛出的对象:

try  {

  breedMoreLlamas();

}  on  OutOfLlamasException  {

  // 一个具体异常

  buyMoreLlamas();

}  on Exception catch  (e)  {

  // 任何其它异常

  print('Unknown exception: $e');

}  catch  (e)  {

  // 无具体类型,处理所有类型

  print('Something really unknown: $e');

}

如以上代表所示,可以使用 oncatch 或者同时使用。在需要指定异常类型时使用 on 。在你的异常处理器需要异常对象时使用 catch

可以为catch()指定一个或两个参数。第1个是抛出的异常,第二是栈跟踪 (一个 StackTrace对象).

try  {

  // ···

}  on Exception catch  (e)  {

  print('Exception details:\n $e');

}  catch  (e,  s)  {

  print('Exception details:\n $e');

  print('Stack trace:\n $s');

}

要部分处理异常,但允许其执行,使用 rethrow 关键字。


void  misbehave()  {

  try  {

    dynamic foo  =  true;

    print(foo++);  // 运行时错误

  }  catch  (e)  {

    print('misbehave() partially handled ${e.runtimeType}.');

    rethrow;  // 允许调用者查看该异常

  }

}

void  main()  {

  try  {

    misbehave();

  }  catch  (e)  {

    print('main() finished handling ${e.runtimeType}.');

  }

}

Finally

要确保一些代码不管是否抛出异常时都执行,使用 finally 从句。如果没有 catch 从句匹配该异常,在finally从句执行后会推出异常。

try  {

  breedMoreLlamas();

}  finally  {

  // 即使在没有抛出异常时也会清理

  cleanLlamaStalls();

}

finally 从任意匹配的catch 从句之后执行:

try  {

  breedMoreLlamas();

}  catch  (e)  {

  print('Error: $e');  // 首先处理异常

}  finally  {

  cleanLlamaStalls();  // 然后进行清理

}

阅读库导览的 异常 的一节了解更多信息。

Dart是一个带有类和基于mixin继承的面向对象语言。每个对象都是类的实例,所有类都继承自 Object.。* 基于mixin的继承* 表示虽然每个类(除Object外)都只有一个超类,一个类主体可在多个类级别中复用。

使用类成员

对象有由函数和数据(分别为方法和实例变量)组成的成员。在调用方法时,对一个对象进行调用:该方法可访问对象的函数和数据。

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

var  p  =  Point(2,  2);

// 设置实例变量y的值

p.y  =  3;

// 获取 y 的值

assert(p.y  ==  3);

// 对 p 调用 distanceTo()

num distance  =  p.distanceTo(Point(4,  4));

使用 ?. 代替 . 来避免最左侧操作项为null时的异常:


// 如果p 不为null, 设置它的y 值为 4

p?.y  =  4;

使用构造函数

可以使用构造函数创建对象。构造函数名可为 *ClassName**ClassName*.*identifier*。例如,如下代码使用Point()Point.fromJson() 构造函数创建 Point 对象:

var  p1  =  Point(2,  2);

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

以下代码有同样的效果,但在构造函数名前使用可选的关键字 new

var  p1  =  new  Point(2,  2);

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

Version note: 关键字 new Dart 2中成为可选项。

一些类提供 常量构造函数。要使用常量构造函数创建编译时常量,在构造函数名前放置关键字 const

var  p  =  const  ImmutablePoint(2,  2);

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

var  a  =  const  ImmutablePoint(1,  1);

var  b  =  const  ImmutablePoint(1,  1);

assert(identical(a,  b));  // 它们是相同的实例!

在常量上下文中,可以在构造函数或字面量前省略 const 。例如,查看如下创建一个const映射的代码:

// 这里有很多const 关键字

const  pointAndLine  =  const  {

  'point':  const  [const  ImmutablePoint(0,  0)],

  'line':  const  [const  ImmutablePoint(1,  10),  const  ImmutablePoint(-2,  11)],

};

可以省略掉第一次使用之外的所有 const 关键字:


// 仅一个const, 它创建常量上下文

const  pointAndLine  =  {

  'point':  [ImmutablePoint(0,  0)],

  'line':  [ImmutablePoint(1,  10),  ImmutablePoint(-2,  11)],

};

如果常量构造函数在常量上下文之外且未使用 const调用,它创建一个非常量对象。

var  a  =  const  ImmutablePoint(1,  1);  // 创建一个常量

var  b  =  ImmutablePoint(1,  1);  // 不创建常量

assert(!identical(a,  b));  // 不是相同的实例!

Version note: 关键字 const 在Dart 2的常量上下文成为可选。

获取一个对象的类型

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


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

到这里,你已学习了如何使用类。本节后面的部分展示如何实现类。

实例变量

以下是如何声明实例变量:


class  Point  {

  num  x;  // 声明实例变量 x, 初始值为 null

  num  y;  // 声明 y, 初始值为 null

  num  z  =  0;  // 声明 z, 初始值为 0

}

所有未初始化的实例变量的值为 null

所有的实例变量生成一个getter 方法。非final实例变量也生成一个隐式setter 方法。更多详情,参见getters和setters

class  Point  {

  num  x;

  num  y;

}

void  main()  {

  var  point  =  Point();

  point.x  =  4;  // 使用针对x 的setter方法

  assert(point.x  ==  4);  // 使用针对x 的getter方法

  assert(point.y  ==  null);  // 默认值为null

}

如果在其声明处实例化实例变量(而非在构造函数或方法中进行),在实例创建时设置值,即在构造函数及期初始化程序列表执行前。

本文首发地址:Alan Hou的个人博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容