Language Tour

来自Dart 语言
XsLiDian讨论 | 贡献2012年4月21日 (六) 09:38的版本 (以“<h1> Dart 语言一览 </h1> <p> 欢迎参观 Dart 语言! 在这里您将看到 Dart 各项主要功能的用法, 包括变量、运算符、类、库等。 本...”为内容创建页面)

(差异) ←上一版本 | 已认可版本 (差异) | 最后版本 (差异) | 下一版本→ (差异)
跳转至: 导航搜索

Dart 语言一览

欢迎参观 Dart 语言! 在这里您将看到 Dart 各项主要功能的用法, 包括变量、运算符、类、库等。 本文假定您已经了解如何用其他语言编程。

提示: 如需尝试各项功能,请使用 Dart Editor 创建服务器应用项目。

如需详细了解某项语言功能,请参考 Dart 语言规范

目录

基本的 Dart 程序

以下代码 用到了 Dart 最基本的一些功能。

main() {
  // 变量及取值
  int number   = 42;
  String text     = "number 的数值为";

  // 控制台输出
  print("$text $number.");
}

下面讲解该程序中大部分 Dart 应用都能用到的部分:

main()

最特别的,必需, 应用开始执行时的顶级函数。

//

表示该行剩下的部分为#注释。 也可以这样写:

/* 可以超过一行的注释内容 */

= int, String

通过静态类型说明声明变量。

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

字符串。

$变量名

字符串内插字符串或变量的 toString() 值。

print()

显示输出的简便途径。

样式

Our code follows the conventions in the <a href="/articles/style-guide/">Dart Style Guide</a>. For example, we indent two spaces by convention.

Runtime 模式

Dart programs can run in either production mode or checked mode.

Production mode is the default runtime mode of a Dart program, optimized for speed. In production mode, the optional static types are ignored.

Checked mode is a developer friendly mode that helps you catch some type errors during runtime. For example, if you assign a non-string to a variable declared as a String, an exception will be thrown.

We recommend that you develop and debug in checked mode, and deploy to production mode.

</section>

变量

Here's an example of creating a variable and assigning a value to it:

var name = 'Bob';


Variables are references. The variable named name contains a reference to a String object with a value of "Bob".

Default value

Uninitialized variables have an initial value of null. This includes numbers, which are also objects.

num lineCount;
lineCount == null; // true


Optional types

You have the option of adding static types to your variable declarations:

String name = 'Bob';


Adding types is a good way to clearly express your intent. Tools like compilers and editors can use these types to help you, by providing early warnings for bugs and code completion.

final

If you never intend to change a variable, use final instead of var or in addition to a type. Once a final variable is set, it can't be changed.

final String name = 'Bob';
<span class="code-error">name = 'Alice'; // compile ERROR (VM or to JavaScript)</span>


Summary

Dart variables are optionally typed, though we generally recommend using types. Variables can be marked as final, locking the value. Unitialized variables have an initial value of null.

</section>

<section>

内置类型

The Dart language has special support for the following types:

  • <a href="#strings">strings</a>
  • <a href="#numbers">numbers</a>
  • <a href="#booleans">booleans</a>
  • <a href="#lists">lists</a> (also known as arrays)
  • <a href="#maps">maps</a>

You can initialize an object of any of these special types using a literal. For example, 'this is a string' is a string literal, and true is a boolean literal.

Because every variable in Dart is an object—an instance of a class—you can usually use constructors to create variables. Variables for the built-in types are no different. For example, you can use the Map() constructor to create a map, using the code new Map().

<section>

Strings

A Dart string is a sequence of Unicode character codes. You can use either single or double quotes to create a string:

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 just use the other string delimiter.";

You can put the value of an expression inside a string by using ${expression}. If the expression is a variable, you can skip the {}.

var s = 'string interpolation';

print('Dart has $s, which is very handy.');
print('That deserves all caps. ${s.toUpperCase()} is very handy!');

You can concatenate strings using adjacent string literals:

var s = 'string ''concatenation'
        " works even over line breaks ";
print(s); // string concatenation works even over line breaks

Another way to create a multi-line string: use a triple quote with either single or double quotation marks.

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

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


You can create a "raw" string by prefixing it with @.

var s = @'In a raw string, even \n is ignored.';


As for all objects, you can check whether two strings are equivalent (have the same characters) using the == operator:

var name = 'NAME';
var greeting = "Hello, $name!";
var greetingTemplate = 'Hello, NAME!';

print(greeting == greetingTemplate); // true; they have the same characters


<section id="strings-string">

String methods

Each string literal has the type <a href="http://api.dartlang.org/dart_core/String.html">String</a>. String has some handy methods, including some that let you determine whether a string starts with, ends with, or contains another string.

var fullName = 'Cuthbert Musgrave Girdlestone, III';

fullName.startsWith('Cuthbert');            // true
fullName.endsWith('III');                   // true
fullName.contains(new RegExp('Musgrave'));  // true

Strings are immutable objects, which means you can create them but you can't change them. If you look closely at the <a href="http://api.dartlang.org/dart_core/String.html">String API docs</a>, you'll notice that none of the methods actually changes the state of a String. For example, the method replaceAll() returns a new String without changing the original String.

var greetingTemplate = 'Hello, NAME!';
var greeting = greetingTemplate.replaceAll(new RegExp("NAME"), 'Bob');
print(greeting == greetingTemplate); // false; greetingTemplate didn't change


<section id="strings-buffer">

StringBuffer methods

To programmatically generate a string, you can use <a href="http://api.dartlang.org/dart_core/StringBuffer.html">StringBuffer</a>. A StringBuffer doesn't generate a new String object until toString() is called.

var sb = new StringBuffer();

sb.add("Use a StringBuffer");
sb.addAll(["for ", "efficient ", "string ", "creation "]);
sb.add("if you are ").add("building lots of strings.");

var fullString = sb.toString();

print(fullString); // use a StringBuffer for efficient string creation
                   // if you are building lots of strings

sb.clear();        // all gone!


<aside class="note"> Note: StringBuffers are currently slow when compiled to JavaScript. See bug #<a href="http://code.google.com/p/dart/issues/detail?id=1216">1216</a> for details. </aside>

</section>

</section>

<section>

Numbers

Dart numbers come in two flavors:

<a href="http://api.dartlang.org/dart_core/int.html">int</a>
Integers of arbitrary size
<a href="http://api.dartlang.org/dart_core/double.html">double</a>
Decimal 64-bit doubles, as specified by the IEEE 754 standard

Both int and double are subinterfaces of <a href="http://api.dartlang.org/dart_core/num.html">num</a>. The num interface defines basic operators such as +, -, /, and *, as well as bitwise operators such as >>. The num interface is also where you'll find abs(), ceil(), and floor(), among other methods. If num and its subinterfaces don't have what you're looking for, the <a href="http://api.dartlang.org/dart_core/Math.html">Math</a> class might.

Integers are numbers without a decimal point. Here are some examples of defining integer literals:

var x = 1;
var hex = 0xDEADBEEF;
var bigInt = 3465346583465243765923847659234765928347659567398475647495873984572947593470294387093493456870849216348723763945678236420938467345762304958724596873045876234572037862934765294365243652548673456705673465273465246734506873456729457623845623456234650457693475603768922346728346256;


If a number includes a decimal, it is a double. Here are some examples of defining double literals:

var y = 1.1;
var exponents = 1.42e5;

Here's how you turn a string into a number, or vice versa:

// string -> int
var one = Math.parseInt("1");                   // 1

// string -> double
var onePointOne = Math.parseDouble("1.1");      // 1.1

// int -> string
String oneAsString = 1.toString();              // "1"

// double -> string
String piAsString = 3.14159.toStringAsFixed(2); // "3.14"


</section>

<section>

Booleans

Dart has a formal boolean type, named bool. Only two objects have type bool: the boolean literals, true and false.

When Dart expects a boolean value, if that value isn't true, then it is false. Unlike in JavaScript, values such as 1 or non-null objects are not treated as true.

For example, consider the following code:

var name = 'Bob';
if (name) {
  print("You have a name!"); // prints in JavaScript, not in Dart
}

In JavaScript, this code prints "You have a name!" because name is a non-null object. However, in Dart, the above doesn't print at all because name is converted to false because name != true.

Here's another example of code that behaves differently in JavaScript and Dart:

if (1) {
  print("JavaScript prints this line because it thinks 1 is true.");
} else {
  print("Dart prints this line because it thinks 1 is NOT true.");
}


<aside class="note"> Note: Dartboard currently has the wrong behavior for the previous two samples: it prints the same string that JavaScript would. (See bug #<a href="http://code.google.com/p/dart/issues/detail?id=1190">1190</a> for details.) Also note that, unlike most of the samples in this tour, the previous two samples don't work in checked mode. </aside>

Dart's treatment of booleans is designed to avoid the strange behaviors that can arise when many values can be treated as true. What this means for you is that, instead of using code like if (nonboolean_value), you should instead explicitly check for values. For example:

// check for an empty string
var fullName = '';
if (fullName.isEmpty()) {
  print("Please enter a full name");
}

// check for zero
var hitPoints = 0;
if (hitPoints == 0) {
  print("Uh oh! Looks like you died.");
}

// check for null
var unicorn = null;
if (unicorn == null) {
  print("You didn't wish hard enough. Wish harder.");
}

// check for NaN
var iMeantToDoThis = 0/0;
if (iMeantToDoThis.isNaN()) {
  print("0/0 is not a number.");
}


</section> </section>

<section>

Lists (also known as arrays)

Perhaps the most common collection in nearly every programming language is the array, or ordered set of objects. In Dart, arrays are List objects, so we usually just call them lists. When you compile Dart to JavaScript, a Dart list compiles to a JavaScript array.

Dart list literals look like JavaScript array literals. Here's a simple Dart list:

var list = [1,2,3];

You can get a list's length and refer to list elements just as you would in JavaScript:

var list = [1,2,3];
print(list.length); // the # of elements: 3
print(list[1]);     // the second item: 2

You can add an element to a list using the add() method:

var list = [1,2,3];
list.add(4);

To remove elements from a list (reducing the list's size), use the removeRange() method:

var list = [1,2,3,4];
list.removeRange(2, 1); // remove the third element

<section id="list-iterate">

Iterating

When you need to work on each element of a list, you can use for, for...in, or forEach(). Use for when you need the current iteration index:

var list = [1,2,3];
for (var x = 0; x < list.length; x++) {
  print('$x: ${list[x]}');
}

If you don't need the index, you can use for...in:


var list = [1,2,3];
for (final x in list) {
  print(x);
}

If you just want to apply a function to each element of the list, use the forEach() method:

var list = [1,2,3];
void printElement(element) => print(element);
list.forEach(printElement);

Or, more succintly:

var list = [1,2,3];
list.forEach((element) => print(element));

</section>


<section id="list-collection">

List and Collection methods

The <a href="http://api.dartlang.org/dart_core/Collection.html#forEach">forEach()</a> method is just one of many handy methods defined by the <a href="http://api.dartlang.org/dart_core/List.html">List</a> interface and its superinterface, <a href="http://api.dartlang.org/dart_core/Collection.html">Collection</a>. For example, the <a href="http://api.dartlang.org/dart_core/Collection.html#filter">filter()</a> method returns a new collection with only the elements that satisfy a condition. The <a href="http://api.dartlang.org/dart_core/Collection.html#every">every()</a> and <a href="http://api.dartlang.org/dart_core/Collection.html#some">some()</a> methods check whether a collection matches every condition or at least one condition, respectively. The <a href="http://api.dartlang.org/dart_core/List.html#sort">sort()</a> method lets you sort a list using any criteria you like.

For more information about lists, see <a href="#generics">Generics</a>.

</section> </section>

<section>

Maps

In general, a map is an object that associates keys to values. Dart support for maps is provided by map literals and the <a href="http://api.dartlang.org/dart_core/Map.html">Map</a> interface.

Here's a simple Dart map:

var gifts = {                         // a map literal
// keys       values
  "first"  : "partridge",
  "second" : "turtledoves",
  "fifth"  : "golden rings"};

In map literals, each key must be a string. If you use a Map constructor, then you have more options: the key can be a string, a number, or any other object that implements the <a href="http://api.dartlang.org/dart_core/Hashable.html">Hashable</a> interface.

var map = new Map();                  // use a Map constructor
map[1] = "partridge";                 // key is 1; value is "partridge"
map[2] = "turtledoves";               // key is 2; value is "turtledoves"
map[5] = "golden rings";              // key is 5; value is "golden rings"

A map value can be any object or null.

You add a new key-value pair to an existing map just as you would in JavaScript:

var gifts = { "first": "partridge" };
<b>gifts["fourth"] = "calling birds";</b>    // add a key-value pair

You retrieve a value from a map the same way you would in JavaScript:

var gifts = { "first": "partridge" };
print(gifts['first']);                // partridge

If you look for a key that isn't in a map, you get a null in return. However, because values can be null, you might need to use a method such as <a href="http://api.dartlang.org/dart_core/Map.html#containsKey">containsKey()</a> or <a href="http://api.dartlang.org/dart_core/Map.html#putIfAbsent">putIfAbsent()</a> to make sure you interpret null correctly.

var gifts = { "first": "partridge" };
print(gifts['fifth']);                // null

Use .length to get the number of key-value pairs in the map:

var gifts = { "first": "partridge" };
gifts["fourth"] = "calling birds";
print(<b>gifts.length</b>);                  // 2

To remove a key-value pair from a map, use the remove() method:

var gifts = { "first": "partridge" };
gifts["fourth"] = "calling birds";
<b>gifts.remove('first');</b>
print(gifts.length);                  // 1
print(gifts['first']);                // null

You can copy a map using the Map.from() constructor:

var gifts = { "first": "partridge" };
var regifts = new Map.from(gifts);
print(regifts['first']);              // partridge


<section id="maps-iterating">

Iterating

You have a few choices for iterating through the contents of a map. Using the forEach() method gives you access to both the key and the value.

var gifts = {
  "first" : "partridge",
  "second": "turtledoves",
  "fifth" : "golden rings"};
gifts.forEach((k,v) => print('$k : $v'));

<aside class="note"> Note: Don't depend on forEach() returning the key-value pairs in a particular order. </aside>

If you are interested in just the keys or just the values, use getKeys() or getValues(), respectively. Both methods return a <a href="http://api.dartlang.org/dart_core/Collection.html">Collection</a> object.

var gifts = {"first": "partridge", "second": "turtledoves"};
var values = gifts.getValues();
values.forEach((v) => print(v));      // partridge, turtledoves

<aside class="note"> Note: Map itself does not extend the Collection interface. </aside>

</section>

</section>

<section>

Summary of built-in types

Dart's <a href="#built-in-types">built-in types</a> all have special literals and implement a built-in interface. For example, numbers have literals such as 1 and 1.1, and they implement the num interface.

You often use literals to create objects of most built-in types, but you can also use constructors. Booleans are unusual because you can't create new objects of type bool; you're stuck with true and false.

For more information about maps and lists, see <a href="#generics">Generics</a>.

</section>

</section>

<section>

函数

Here's a simple function:

String say(String from, String msg) => "$from says $msg";


And here's an example of calling it:

print(say("Bob", "Hello")); // "Bob says Hello"


Omitting the types, you could write the above as:

say(from, msg) => "$from says $msg";


However, we recommend using types for function signatures.

The => e; syntax is a shorthand for { return e; }. For example, say(from, msg) => "$from says $msg"; is the same as:

say(from, msg) {
  return "$from says $msg";
}


Optional parameters

Wrapping a function parameter in [] marks it as an optional parameter.

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


Here's an example of calling this function without the optional parameter:

print(say("Bob", "Howdy")); // Bob says Howdy


Here's an example of calling this function with the third parameter:

print(say("Bob", "Howdy", "smoke signal"));
// Bob says Howdy with a smoke signal


Default values for optional parameters

Optional parameters may have default values. The default values must be compile time constants. If no default value is provided, the value is null (as we saw above).

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


Omitting the optional parameter, you can see how the default value is used:

print(say("Bob", "Howdy")); // Bob says Howdy with a carrier pigeon


Named parameters

Optional parameters are also named parameters.

print(say("Bob", "Howdy", device: "tin can and string"));
// Bob says Howdy with a tin can and string


First class functions

You can pass a function as a parameter to another function. For example:

List ages = [1,4,5,7,10,14,21];
List oddAges = ages.filter((i) => i % 2 == 1);


Which is the same as:

bool isOdd(num i) => i % 2 == 1;
List ages = [1,4,5,7,10,14,21];
List oddAges = ages.filter(isOdd);


You can also assign a function to a variable, such as:

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


Lexical closures

Functions can close over variables defined in surrounding scopes. The below example shows how makeAdder captures the variable n and makes it available to the function that makeAdder returns. Whereever the returned function goes, it remembers n.

Function makeAdder(num n) {
  return (num i) => n + i;
}

main() {
  var add2 = makeAdder(2);
  print(add2(3)); // 5
}


(Special thanks to Bob Nystrom for this example.)

Return values

All functions return a value. If no return value is specified, the statement return null; is implicitly appended to the function body.

Summary of functions

Dart supports first class functions, with optional parameters, named parameters, and default values for parameters. Functions may be assigned to variables and passed as parameters to other functions. Functions also support lexical closures, which allows access to variables outside its immediate lexical scope.

</section>

<section>

运算符

Dart does operators. Not only does it define them, but it lets you redefine many of them.

The following table shows all of Dart's operators, in order of precedence.

Description Operator
unary postfix expr++ expr-- () [] .
unary prefix -expr !expr ~expr ++expr --expr
multiplicative * / % ~/
additive + -
shift << >>
relational is is! >= > <= <
equality == != === !==
bitwise AND &
bitwise XOR ^
bitwise OR |
logical AND &&
logical OR ||
conditional expr ? expr : expr
assignment = *= /= ~/= %= += -= <<= >>= &= ^= |=


For example, the % operator has higher precedence than (and thus executes before) the == operator, which has higher precedence than the && operator. That precedence means that the following two lines of code execute the same way:

if ((n % i == 0) && (d % i == 0)) // parens improve readability
if (n % i == 0 && d % i == 0)     // harder to read, but equivalent


This section covers the following topics:

  • <a href="#op-arithmetic">arithmetic operators</a>
  • <a href="#op-equality">equality and relational operators</a>
  • <a href="#op-assign">assignment operators</a>
  • <a href="#op-logical">logical operators</a>
  • <a href="#op-bit">bitwise and shift operators</a>
  • <a href="#op-other">other operators</a>
  • <a href="#op-methods">operators as methods</a>


<section id="op-arithmetic">

Arithmetic operators

Dart supports the usual arithmetic operators.

Operator Meaning
+ add
subtract
-expr unary negation (reverse the sign of the expression)
* multiply
/ divide
~/ divide, returning an integer result
 % get the remainder


Example:

int a = 2;
int b = 3;

print('${a + b}');  // 5
print('${a - b}');  // -1
print('${a * b}');  // 6
print('${a / b}');  // 0.6666666666666666
print('${a ~/ b}'); // 0 (quotient)
print('${a % b}');  // 2 (remainder)


Dart also supports both prefix and postfix increment and decrement operators.

Operator Meaning
++var var = var + 1 (expression value is var + 1)
var++ var = var + 1 (expression value is var)
--var var = var – 1 (expression value is var – 1)
var-- var = var – 1 (expression value is var)


Example:

int a = 2;

print('${ ++a }'); // 3 (increment before returning a value)
print('${ a++ }'); // 3 (increment after returning a value)
print('${ a-- }'); // 4 (decrement after returning a value)
print('${ --a }'); // 2 (decrement before returning a value)


</section>

<section id="op-equality">

Equality and relational operators

Operator Meaning
== equal (see discussion below)
 != not equal
=== same instance
 !== not the same instance
> greater than
< less than
>= greater than or equal to
<= less than or equal to
is true if the object has the specified type (see discussion below)
is! false if the object has the specified type


To test whether two objects x and y represent the same thing, use the == operator. You don't usually need to use the === operator, which tests whether two objects are, in fact, the exact same object. Here's how the == operator will work:

  1. If x===y, return true.
  2. Otherwise, if either x or y is null, return false.
  3. Otherwise, return the result of x.equals(y).


The is and is! operators are handy for checking types. The result of obj is T is true if obj implements the interface specified by T. For example, obj is Object is always true.

Here's an example of using each of the equality and relational operators:

int a = 2;
int b = 3;
int c = a;

print(a == 2);       // true; 2 and 2 are equal
print(a != b);       // true; 2 and 3 aren't equal
print(a === c);      // true; a and c are the same object
print(a !== b);      // true; 2 and 3 aren't the same object
print(b > a);        // true; 3 is more than 2
print(a < b);        // true; 2 is less then 3
print(b >= b);       // true; 3 is greater than or equal to 3
print(a <= b);       // true; 2 is less than or equal to 3
print(a is num);     // true; 2 is a number
print(a is! String); // true; 2 is an int, not a string


</section>

<section id="op-assign">

Assignment operators

You assign values using the = operator. You can also use compound assignment operators, which combine an operation with an assignment.

Compound assignment Equivalent expression
For an operator op: a op= b a = a op b
Example: a += b a = a + b


Here's a full list of the assignment operators:

=
+=
–=

  • =

/=
~/=
%=
<<=
>>=
&=
^=
|=


The following example uses both assignment and compound assignment operators:

int a = 2;           // assign using =

a *= 3;              // assign and multiply: a = a * 3
print('a *= 3: $a'); // a *= 3: 6


</section>

<section id="op-logical">

Logical operators

You can invert or combine boolean expressions using the logical operators.

Operator Meaning
 !expr inverts the following expression (changes false to true, and vice versa)
|| logical OR
&& logical AND



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


</section>

<section id="op-bit">

Bitwise and shift operators

You can manipulate the individual bits of objects in Dart. Usually, you'd use these operators with integers.

Operator Meaning
& AND
| OR
^ XOR
~expr unary bitwise complement (0s become 1s; 1s become 0s)
<< shift left
>> shift right


Here's an example of using bitwise and shift operators.

int value = 0x22;
int bitmask = 0x0F;

print(value);                                // 34 (0x22)
print(value.toRadixString(16));              // 22
print(value.toRadixString(2));               // 100010
print((value & bitmask).toRadixString(16));  // 2  (AND)
print((value & ~bitmask).toRadixString(16)); // 20 (AND NOT)
print((value | bitmask).toRadixString(16));  // 2f (OR)
print((value ^ bitmask).toRadixString(16));  // 2d (XOR)
print((value << 4).toRadixString(16));       // 220
print((value >> 4).toRadixString(16));       // 2


</section>

<section id="op-other">

Other operators

Operator Name Meaning
() function application represents a function call
[] list access refers to the value at the specified index in the list
expr1 ? expr2 : expr3 conditional if expr is true, executes expr;
     otherwise, executes expr
     (technically special syntax, not an operator)
. member access refers to a property of an expression;
     example: foo.bar selects property bar
from expression foo


</section>

<section id="op-methods">

Operators are methods

Operators are just instance methods with special names. For example, the expression 1 + 2 invokes the + method on 1, with the argument 2—something like 1.+(2). This has a couple of consequences:

  • Dart lets you override many operators. For example, if you define a Vector class, you might define a + method to add two vectors.
  • For operators that work on two operands, the leftmost operand determines which version of the operator is used. For example, if you define a Vector class and a Point class, aVector + aPoint uses the Vector version of +. </section>


The following operators can be overridden:

<
>
<=
>=

+
/
~/


%
|
^
&
<<
>>
[]
[]= (list assignment)
~
equals() (==) *


<aside class="note">

* The == operator can currently be overridden, but not for long. Soon the way to customize the behavior of == will be by overriding the equals() method. </aside>

For an example of overriding operators, see <a href="#classes-operators">Operators</a> in the Classes section.

</section>

<section id="op-summary">

Summary of operators

Dart operators should look and act familiar. Behind the scenes, an operator is a specially named method that's invoked on its first operand. As a result, operand order can make a difference: a+b might not give the same result as b+a. You can override many operators. </section>

</section>

<section>

流程控制

You can control the flow of your Dart code using any of the following:

  • <a href="#if-else">If and else</a>
  • <a href="#for-loops">For loops</a>
  • <a href="#while">While and do while</a>
  • <a href="#break">Break and continue</a>
  • <a href="#switch">Switch and case</a>


If and else



if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}


Remember, unlike JavaScript, Dart treats all values that are not true as false. See <a href="#booleans">Booleans</a> for more info.

For loops


You can iterate with the standard for loop.

for (int i = 0; i < candidates.length; i++) {
  candidates[i].interview();
}


Closures inside of Dart's for loops correctly capture the value of the index, avoiding a common pitfall found in JavaScript. For example, consider:

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


The output is 0 and then 1, as expected. In contrast, the example would print 2 and then 2 in JavaScript.

If the object that you are iterating over is a Collection, you can use the <a href="http://api.dartlang.org/dart_core/Collection.html#forEach">forEach()</a> method. Using forEach() is a good option if you don't need to know the current iteration counter.

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


Collections also support the for-in form of iteration:

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



While and do while


A while loop evaluates the conditional before the loop.

while (!auctionItem.currentWinner(bidder) &&
       auctionItem.currentBid < bidder.maximumBid) {
  auctionItem.placeBid(bidder, auction.currentBid + 1);
}


A do while loop evaluates the conditional after the loop.

do {
  printLine();
} while (!atEndOfPage());



Break and continue


Use break to stop looping.

while (true) {
  if (shutDownRequested()) break;
  processIncomingRequests();
}


Use continue to skip to the next loop iteration.

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


You might write that example differently if you're using a <a href="http://api.dartlang.org/dart_core/Collection.html">Collection</a>.

candidates.filter((c) => c.yearsExperience >= 5)
          .forEach((c) => c.interview());



Switch and case


Switch statements in Dart compare objects using ==. Remember to include a break statement at the end of each non-empty case clause to avoid fall-through (which is an error, see below). A default clause can be used to catch conditions that don't match.

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClose();
    break;
  case 'PENDING':
    executePending();
    break;
  case 'APPROVED':
    executeApproved();
    break;
  case 'DENIED':
    executeDenied();
    break;
  case 'OPEN':
    executeOpen();
    break;
  default:
    executeUnknown();
}


The following example omits the break statement in the case clause, thus generating an error:

var command = 'OPEN';
switch (command) {

  case 'OPEN':
    executeOpen();
    // ERROR: missing break causes an exception to be thrown!!

  case 'CLOSED':
    executeClose();
    break;
}


However, Dart does support empty case clauses, allowing a form of fall-through.

var command = 'CLOSED';
switch (command) {
  case 'CLOSED':     // empty case falls through
  case 'NOW_CLOSED':
    // runs for both CLOSED and NOW_CLOSED
    executeClose();
    break;
}


</section>

<section>

异常处理

Your Dart code can throw and catch exceptions. Exceptions are errors that signal something happened that was not anticipated. If not caught, exceptions bubble up to the top of the program.

In contrast to Java, all of Dart's exceptions are unchecked exceptions. Methods do not declare which exceptions they might throw, and you are not required to catch any exceptions.

Dart provides an <a href="http://api.dartlang.org/dart_core/Exception.html">Exception</a> interface and numerous predefined exception types. You can, of course, define your own exceptions by extending the Exception interface. Some examples of common exceptions include:


However, Dart programs can throw any object as an exception.

Throw

Here's how you throw, or raise, an exception.

throw new IllegalArgumentException('Value must be greater than zero');


You can also throw arbitrary objects.

throw "Out of llamas!";


Catch

Catching, or capturing, an exception stops the exception from propagated. Catching an exception gives you a chance to handle it.

try {
  breedMoreLlamas();
} catch (final OutOfLlamasException e) {
  buyMoreLlamas();
}


To handle code that can throw more than one type of exception, you can specify multiple catch clauses. The first catch clause that matches the thrown object's type handles the exception. If the catch clause does not specify a type, that clause can handle any type of thrown object.

try {
  breedMoreLlamas();
} catch (final OutOfLlamasException e) {  // a specific exception
  buyMoreLlamas();
} catch (final Exception e) {             // anything that is an exception
  print("Unknown exception: $e");
} catch (final e) {                       // no specified type, handles all
  print("Something really unknown: $e");
}


Finally

To ensure that some code runs whether or not an exception is thrown, use the finally clause.

If no catch clause matches the exception, the finally clause runs and then the exception is propagated.

try {
  breedMoreLlamas();
} finally {
  cleanLlamaStalls();  // always run, even if exception is thrown
}


The finally clause runs after any matching catch clauses.

try {
  breedMoreLlamas();
} catch (final e) {
  print("Error: $e");  // handle exception first
} finally {
  cleanLlamaStalls();  // then run finally
}


</section>

<section>

Dart is an object-oriented language with classes and single inheritance. The root class is <a href="http://api.dartlang.org/dart_core/Object.html">Object</a>.

Instance variables

Here's how you declare a class with instance variables (also known as member variables):

class Point {
  num x, y;
}


All uninitialized instance variables have the value null.

Both final and non-final instance variables generate an implicit getter method. Non-final instance variables generate an implicit setter method. (<a href="#classes-getters-and-setters">Getters and setters</a> are discussed more later.)

main() {
  var point = new Point();

  // use the setter method for x
  point.x = 4;

  // use the getter method for x
  print(point.x);  // 4

  // values default to null
  print(point.y);  // null
}


Instance variable initialization

If you initialize an instance variable where it is declared (instead of in a constructor or method), the initial value must be a compile-time constant.

<aside class="note"> Note: This restriction is currently under review. </aside>

An example of a compile-time constant is a number literal:

class Point {
  num x = 0,
      y = 0;
}


Use the constructor body, demonstrated in the next section, to assign non-constant values to instance variables.

Constructors

Declare a constructor by creating a method with the same name as its class. The most common form of constructor, the generative constructor, creates a new instance of a class.

class Point {
  num x, y;

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


The this keyword references the current instance.

<aside class="note"> Note: Use this only when there is a name conflict. Otherwise, Dart style omits the this. </aside>

The pattern of assigning a constructor argument to a member variable is so common, Dart has syntactic sugar to make it easy.

class Point {
  num x, y;

  // syntactic sugar for this.x = x and this.y = y
  Point(this.x, this.y);
}


Default constructors

If you don't declare a constructor, a default constructor is provided for you. The default constructor has no arguments and invokes the the no-argument constructor in the superclass.

Initializer list

Final variables must be initialized before the object is assigned to this. Use the initializer list, which runs before the constructor body, to initialize any final variables.

#import('dart:html');

class Button {
  final Collection<ButtonHandler> handlers;
  final String domId;
  final Element elem;
  
                // what follows the : is the initializer list
  Button(domId) : domId = domId,
                  handlers = [],
                  elem = document.query(domId) {
    bindHandlers();
  }

  bindHandlers() {
   // ...
  }
}


The right-hand side of an initializer does not have access to this.

Named constructors

Use a named constructor to implement multiple constructors for a class or to provide extra clarity.

class Point {
  num x, y;

  // named constructor
  Point.fromJson(Map json) : x = json['x'], y = json['y'];

  Point(this.x, this.y);
}


Create new instances from a named constructor with new:

var jsonData = JSON.parse('{"x":1, "y":2}');
var point = new Point.fromJson(jsonData);


Constant constructors

Dart has deterministic object creation, thus avoiding tricky situations found in other languages. To achieve this, only immutable compile-time expressions are allowed for the initial values of instance variable declarations.

An immutable compile-time constant object is known as a const object. Creating a const object requires defining a const constructor and ensuring all instance fields are final.

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


Because compile-time constants are constant and immutable, constructing two identitical const objects results in a single, canonical instance.

void main() {
  var a = const Point(1, 1);
  var b = const Point(1, 1);

  print(a === b); // true, they are the same instance!
}


<aside class="note"> Note: Other examples of compile-time constants are literal numbers and literal strings. </aside>

Factory constructors

Use the factory keyword when implementing a constructor that doesn't always create a new instance of its class. For example, a factory constructor might return an instance from a cache, or it might return an instance of a subclass.

The following example demonstrates a factory constructor returning objects from a cache.

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

  static Map<String, Logger> _cache;

  factory Logger(String name) {
    if (_cache == null) {
      _cache = {};
    }

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

  Logger._internal(this.name);

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


As for other constructors, to invoke a factory constructor you use the new keyword:

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


<aside class="note"> Note: Factory constructors have no access to this. </aside>

Methods

Methods are functions that provide behavior for an object.

Instance methods

Instance methods on objects can access instance variables and this. The distanceTo() method in the following sample is an example of an instance method.

class Point {
  num x, y;
  Point(this.x, this.y);

  num distanceTo(Point other) {
    return Math.sqrt(((x-other.x)*(x-other.x)) + ((y-other.y)*(y-other.y)));
  }
}


Here's how you invoke the distanceTo() method on an instance of Point:

var point = new Point(2, 2);
num distance = point.distanceTo(new Point(4,4));
print(distance);  // 2.82842...


<section id="classes-getters-and-setters">

Getters and setters

Getter and setter methods provide read and write access to internal object state. When calling a getter or setter, omit the trailing parentheses. Define getters and setters using the get and set keywords.

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

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

  num get right()           => left + width;
      set right(num value)  => left = value - width;
  num get bottom()          => top + height;
      set bottom(num value) => top = value - height;
}


Use explicit getters and setters just like you would use the generated getters and setters from instance variables.

var rect = new Rectangle(3, 4, 20, 15);
print(rect.left); // 3
rect.right = 12;
print(rect.left); // -8


With getters and setters, you can start with instance variables, later wrapping them with methods, all without changing client code.

<section id="classes-operators">

Operators

Because operators are just instance methods with special names, you can override <a href="#op-methods">many operators</a>. Here's an example of a class that overrides the + and - operators.

class Vector {
  final int x,y;
  const Vector(this.x, this.y);

  Vector operator +(Vector v) { // overrides + (a + b)
    return new Vector(x + v.x, y + v.y);
  }
  
  Vector operator -(Vector v) { // overrides - (a - b)
    return new Vector(x - v.x, y - v.y);
  }
  
  Vector operator negate() {    // overrides unary negation (-a)
    return new Vector(-x,-y);
  }
  
  String toString() => '($x,$y)';
}

main() {
  Vector v = new Vector(2,3);
  Vector w = new Vector(2,2);
  print(v);   // (2,3)
  print(-v);  // (-2,-3)
  print(v+w); // (4,5)
  print(v-w); // (0,1)
}


<aside class="note"> Implementation note: Overriding - affects only the binary subtraction operator. To override the unary form of -, you must use the special identifier negate. </aside>

</section> </section>

Abstract classes

Dart will support abstract classes and abstract methods.

As of 2012-04-04, abstract is not yet implemented. Follow bugs <a href="http://code.google.com/p/dart/issues/detail?id=1603">1603</a> and <a href="http://code.google.com/p/dart/issues/detail?id=1605">1605</a> to track the progress.

Extending a class

Use extends to create a subclass, and super to refer to the superclass.

class Television {
  turnOn() {
    _illuminateDisplay();
    _activeIrSensor();
  }
}

class SmartTelevision extends Television {
  turnOn() {
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
}


Subclasses can override instance methods, getters, and setters.

Class-level static members

Use the static keyword to implement class-wide variables and methods.

Static methods

Static methods (class methods) do not operate on an instance, and thus do not have access to this.

class Point {
  num x, y;
  Point(this.x, this.y);

  static num distanceBetween(Point a, Point b) {
    return Math.sqrt(((a.x-b.x)*(a.x-b.x)) + ((a.y-b.y)*(a.y-b.y)));
  }
}

main() {
  var a = new Point(2, 2);
  var b = new Point(4, 4);
  print(Point.distanceBetween(a, b));  // 2.82842...
}


<aside class="note"> Best practice: Consider using top-level functions, instead of static methods, for common or widely used utilities and functionality. </aside>

Static variables

Static variables (class variables) are useful for class-wide state and constants.

class Color {
  static final RED = const Color('red');
  final String name;
  const Color(this.name);
  String toString() => name;
}

main() {
  print(Color.RED); // 'red'
}


</section>

<section>

接口

Interfaces are types that define how you can interact with an object. An interface can specify methods, constructors, instance variables (or, more precisely, getters and setters), and superinterfaces. It doesn't, however, specify the code inside of methods and constructors.

Interfaces are handy for specifying APIs without specifying exactly how the APIs are implemented.

<section id="interfaces-defining">

Defining an interface

Use the interface keyword to define an interface. For example, here's the code that defines the <a href="http://api.dartlang.org/dart_core/Hashable.html">Hashable</a> interface:

interface Hashable {
  int hashCode();
}


Notice how, instead of having a method body ({...}), the interface just has a semicolon (;). </section>

<section id="interfaces-implementing">

Implementing an interface

A class can implement one or more interfaces by declaring them in its implements clause and then providing the APIs required by the interfaces. For example:

class Point <b>implements Hashable</b> {
  num x, y;
  ...
  // required by Hashable
  <b>int hashCode() {</b>
    int result = 17;
    result = 37 * result + x.hashCode();
    result = 37 * result + y.hashCode();
    return result;
  <b>}</b>
  ...
}


Here's an example of specifying that a class implements multiple interfaces:

class Point <b>implements Comparable, Hashable</b> {
  ...
}


<aside class="note"> Note: Soon you'll be able to treat Dart classes as interfaces. This feature will be useful when creating mock objects for testing. </aside>

</section>

<section id="interfaces-extending">

Extending an interface

You can create an interface that builds on one or more interfaces. The new interface is called a subinterface, and all the interfaces it inherits from are its superinterfaces.

Use the extends keyword to specify which interface (or interfaces) you're adding to. Here's an example of creating a subinterface of Hashable:

interface HashablePoint extends Hashable {
  num x, y;
}


<aside class="note"> Note: Technically, interfaces don't have instance variables such as x and y. What looks like an instance variable is really a shortcut for declaring <a href="#classes-getters-and-setters">getter and setter methods</a>. </aside>

Here's an example of implementing a subinterface and checking types.

class Point implements HashablePoint {
  num x, y; // required by HashablePoint

  // required by Hashable
  int hashCode() {
    ...
  }
}

void main() {
  Point p = new Point();
  print(p is Point);          // true
  print(p is Hashable);       // true
  print(p is HashablePoint);  // true
}


</section>

<section id="interfaces-default-class">

Defining constructors and a default class

An interface can define constructors, as long as it specifies a default class.

<aside class="note"> Note: Many of the Dart APIs are implemented as interfaces that have default classes. For example, all of the <a href="#built-in-types">built-in types</a>, including <a href="#numbers">num and int</a>, are interfaces. </aside>

Use the default keyword to specify the default class. For example:

interface ObjectCache default MemoryCache {
  ObjectCache();
  ...
}


The code for the default class might look like this:

class MemoryCache implements ObjectCache {
  ...
}


Invoking a constructor via an interface results in a call to the equivalent constructor in the interface's default class. For example:

var cache = new ObjectCache(); // same as: new MemoryCache()


</section>

<section id="interfaces-summary">

Summary of interfaces

Interfaces are everywhere in Dart, from built-in types to many of the types defined in the standard Dart libraries. Because Dart interfaces can have default classes, you can use interface constructors. </section>

</section>

<section>

泛型

If you look at the API documentation for the basic array type, <a href="http://api.dartlang.org/dart_core/List.html">List</a>, you'll see that the type is actually List<E>. The <...> notation marks List as a generic (or parameterized) type—a type that can declare formal type parameters.

Why use generics?

Because types are optional in Dart, you never have to use generics. You might want to, though, for the same reason you might want to use other types in your code: Types (generic or not) let you document and annotate your code, making it easier to express your intent.

For example, if you intend for a list to contain only strings, you can declare it as List<String> (read that as "List of String"). That way you, your fellow programmers, and your tools (such as Dart Editor and the Dart VM in checked mode) can detect that assigning a non-string to the list is probably a mistake.

List<String> names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
...
names.add(42); // fails in checked mode (succeeds in production mode)


Another reason for using generics is to reduce code duplication. Generics let you share a single interface and implementation between many types, while still taking advantage of checked mode and static analysis early warnings. For example, say you create an interface for caching an object:

interface ObjectCache {
  Object getByKey(String key);
  setByKey(String key, Object value);
}


You discover that you want a string-specific version of this interface, so you create another interface:

interface StringCache {
  String getByKey(String key);
  setByKey(String key, String value);
}


Later, you decide you want a number-specific version of this interface... You get the idea.

Generic types can save you the trouble of creating all these interfaces. Instead, you can create a single interface that takes a type parameter:

interface Cache<T> {
  T getByKey(String key);
  setByKey(String key, T value);
}


In this code, T is the stand-in type. It's a placeholder that you can think of as a type that a developer will define later.

<section id="generics-literals">

Using collection literals

Both built-in collection types are parameterized: <a href="#lists">lists</a> and <a href="#maps">maps</a>. Parameterized literals are just like the literals you've already seen, except that you add <type> before the opening bracket. For example:

List<String> names = <b><String></b>['Seth', 'Kathy', 'Lars'];
Map<String, String> pages = <b><String></b>{ // specify <b>value</b> type: String
    'index.html':'Homepage',  // (the <b>key</b> type is implicitly String)
    'robots.txt':'Hints for web robots',
    'humans.txt':'We are people, not machines' };


<aside class="note"> Note: Map literals always have string keys. The type before the brace specifies the type of the map's values. </aside>

</section>

<section id="generics-constructors">

Using constructors

To specify one or more types when using a constructor, put the types in angle brackets (<...>) just after the class or interface name. For example:

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


The following code creates a map that has integer keys and values of type View:

Map<int, View> views = new Map<b><int, View></b>();


</section>

<section id="generics-collections">

Generic collections and the types they contain

Dart generic types are reified, which is a fancy way of saying that they carry their type information around at runtime. For example, you can test the type of a collection, even in production mode:

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


However, the is expression checks the type of the collection only—not of the objects inside it. In production mode, a List<String> might have some non-string items in it. The solution is to either check each item's type or wrap item-manipulation code in an <a href="#exceptions">exception handler</a>.

<aside class="note"> Note: In contrast, generics in Java use erasure, which means that generic type parameters are removed at runtime. In Java, you can test whether an object is a List, but you can't test whether it's a List<String>. </aside> </section>

<section id="generics-summary">

Summary of generics

For more information about generics, see <a href="http://www.dartlang.org/articles/optional-types/">Optional Types in Dart</a>. </section>

</section>

<section>

库与可访问性

This section explains how to use <a href="#libraries-import">import</a> (#import), <a href="#libraries-source">include</a> (#source), and <a href="#libraries-library">library</a> (#library) directives, which can help you create a modular and shareable code base. Libraries not only provide APIs, but are a unit of privacy: they can hide implementation details such as private variables.

Every Dart app is a library, even if it doesn't use #library.

<aside class="note"> Note: The library system in Dart will change. This section describes how it currently works. </aside>

<section id="libraries-import">

Using libraries

Use #import to specify how a namespace from one library is used in the scope of another library.

Dart web apps generally use the <a href="http://api.dartlang.org/html.html">dart:html</a> library, which they import like this:

#import('dart:html');


The only required argument to #import is a URI specifying the library. For built-in libraries, the URI has the special dart: prefix. For other libraries, the URI can be relative to the current directory. (In the future, you'll be able to specify a file on a web server.) For example:

#import('dart:io');
#import('mylib/mylib.dart');
#import('../../util/utilslib.dart');


<section id="libraries-prefix">

Specifying prefixes

If you import two libraries that have conflicting identifiers, then you can specify a prefix for one or both libraries. For example, if library1 and library2 both define Element, then you might have code like this:

#import('lib1/library1.dart');
#import('lib2/library2.dart', prefix:'lib2');
...
var element1 = new Element();      // uses Element from library1
var element2 = new lib2.Element(); // uses Element from library2


</section> </section>

<section id="libraries-library">

Implementing libraries

Use <a href="#libraries-source">#source</a> (also known as an include directive) to specify the files that are part of the current library, and <a href="#libraries-library">#library</a> to specify that a file implements a library.

<section id="libraries-source">

Associating a file with the current library

You can put your library's implementation into multiple .dart files by using #source in the library's main .dart file.

<aside class="note"> Included files can't have directives. A single file in each library must have all the directives (such as #import and #library) that the library needs. </aside>

For example, consider the following code:

<em>// in Ballgame.dart:</em>
#import('dart:html');

#source('Ball.dart');
#source('Util.dart');
...
main() {
  ...
}


In this example, Ball.dart and Util.dart are compiled into the library Ballgame.dart. Ballgame.dart doesn't have a #library statement, but remember that each Dart app has an implicit library.

<section id="libraries-library">

Declaring a library

To explicitly declare a library, you use a #library statement. For example:

#library('slider_sample');           // declare that this is a library.

#import('dart:html');                // it uses the html library...
#import('../ui_lib/view/view.dart'); // ...and a view library.

#source('SliderSample.dart');        // grab the code from SliderSample.dart.


<aside class="note"> Note: The string in the #library statement isn't currently used. </aside> </section>

<section id="libraries-private-members">

Declaring private members

If an identifier starts with an underscore, it's private to its library. For example, in the following code, the Incrementer class has an instance variable called _hiddenNum.

#library('HidingLibrary');

class Incrementer {
  num _hiddenNum = 0;
  
  void incrementNum() {
    _hiddenNum++;
  }
  
  void printNum() {
    print("The hidden number is $_hiddenNum");
  }
}

void main() {
  var o = new Incrementer();
  print("We can read _hiddenNum (${o._hiddenNum}).");
}


The main() method can refer to _hiddenNum because it's in the same library as Incrementer. But outside its library, _hiddenNum is invisible, even to subclasses of Incrementer.

For example, the following code can't use _hiddenNum because it's in a different library from Incrementer (which is implemented in the library at HidingLib/hider.dart):

#library('SubclassingLibrary');
#import('../HidingLib/hider.dart');

// this class is outside Incrementer's library, so it can't use _hiddenNum
class DoubleIncrementer extends Incrementer {
  void incrementNum() {
    //_hiddenNum = _hiddenNum + 2; // cannot resolve _hiddenNum
    super.incrementNum();          // the only way to increment _hiddenNum
    super.incrementNum();
  }
}


</section>

<section id="libraries-summary">

Summary of libraries and visibility

Use #import if you need to use a library, #source to pull files into a library, and #library to declare that you're implementing a library. Names prefixed with underscore (_) are private to the library. </section>

</section>

</section>

<section>

隔离

All code in Dart runs in the context of an isolate. Use additional isolates for concurrent programming, and to run third-party code more securely.

Isolate concepts

Each isolate has its own heap, which means that all its values in memory, including globals, are available only to that isolate. The only mechanism available to communicate between isolates is to pass messages. Messages are sent through ports.

The content of the message can be:

  • A primitive value (null, num, bool, double, String)
  • An instance of SendPort
  • A list or map whose elements are any of the above, including other lists and maps
  • In <a href="#sending-any-type-of-object">special circumstances</a>, an object of any type


List and maps can have cyclic references to themselves. Each message is copied when sent to another isolate, ensuring one isolate cannot change the state in another isolate.

Isolates might run in a separate process or thread, depending on the implementation. For web applications, isolates can be compiled to Web workers, if they are available.

Using isolates

To use an isolate, you import the isolates library, spawn a new isolate, and then send and receive messages.

Import the isolates library

The isolates API can be found in the dart:isolate library.

#import('dart:isolate');


Spawn an isolate

Any top-level function or static method is a valid entry point for an isolate. The entry point should not expect arguments and should return void. It is illegal to use a function closure as an entry point to an isolate. Pass the entry point to <a href="http://api.dartlang.org/dart_isolate.html#spawnFunction">spawnFunction()</a>.

<aside class="note"> Note: dart2js does not yet support static methods as isolate entry points. </aside>

#import('dart:isolate');

runInIsolate() {
  print("hello from an isolate!");
}

main() {
  spawnFunction(runInIsolate);
}


We plan to support spawning an isolate from code at a URI.

Send messages

Send a message to an isolate via a <a href="http://api.dartlang.org/dart_isolate/SendPort.html">SendPort</a>. The spawnFunction() method returns a handle to its SendPort.

To simply send a message, use <a href="http://api.dartlang.org/dart_isolate/SendPort.html#send">send()</a>.

#import('dart:isolate');

echo() {
  // receive messages here, see next section
}

main() {
  SendPort sendPort = spawnFunction(echo);
  sendPort.send("Hello from main");
}


<section id="sending-any-type-of-object">

Send any type of object

In special circumstances (such as when using spawnFunction() inside the Dart VM), it is possible to send any type of object instance to an isolate. The object message is copied when sent.

Sending arbitrary object instances to an isolate, even if using spawnFunction(), is not yet available when compiling to JavaScript. </section>

Receive messages

Use a <a href="http://api.dartlang.org/dart_isolate/ReceivePort.html">ReceivePort</a> to receive messages sent to an isolate. Obtain a handle to the ReceivePort from the <a href="http://api.dartlang.org/dart_isolate.html#get:port">port</a> property. Then, handle a new message with a callback function passed to the <a href="http://api.dartlang.org/dart_isolate/ReceivePort.html#receive">receive()</a> method.

#import('dart:isolate');

echo() {
  port.receive((msg, SendPort reply) {
    print("I received: $msg");
  });
}

main() {
  SendPort sendPort = spawnFunction(echo);
  sendPort.send("Hello from main");
}


Receive a reply

Use the <a href="http://api.dartlang.org/dart_isolate/SendPort.html#call">call()</a> method on SendPort as a simple way to send a message and receive a reply. The call() method returns a <a href="http://api.dartlang.org/dart_core/Future.html">Future</a> for the reply.

#import('dart:isolate');

echo() {
  port.receive((msg, SendPort reply) {
    reply.send("I received: $msg");
  });
}

main() {
  SendPort sendPort = spawnFunction(echo);
  sendPort.call("Hello from main").then((reply) {
    print(reply);    // I received: Hello from main
  });
}


</section>

<section>

类型定义

In Dart, functions are objects, just as strings and numbers. A typedef, or function-type alias, gives a function type a name that you can use when declaring fields and return types. A typedef retains type information when a function type is assigned to a variable.

Consider the following code, which does not use a typedef.

class SortedCollection {
  Function compare;

  SortedCollection(int f(Object a, Object b)) {
    compare = f;
  }
}

int sort(Object a, Object b) => ... ;

main() {
  SortedCollection collection = new SortedCollection(sort);

  // all we know is that compare is a function, but what type of function?
  print(collection.compare is Function);  // true
}


Type information is lost when assigning f to compare. The type of f is (Object, Object) → int, yet the type of compare is Function. If we change the code to use explicit names and retain type information, both developers and tools can use that information.

Adding a typedef lets Dart retain the type information.

<b>typedef int Compare(Object a, Object b);</b>

class SortedCollection {
  Compare compare;

  SortedCollection(this.compare);
}

int sort(Object a, Object b) => ... ;

main() {
  SortedCollection collection = new SortedCollection(sort);
  print(collection.compare is Function);  // true
  print(collection.compare is Compare);   // true
}


<aside class="note"> Note: Currently, typedefs are restricted to function types. This may change in a future version of the language specification. </aside>

As typedefs are simply aliases, they offer a way to check the type of any function. For example:

typedef int Compare(int a, int b);

int sort(int a, int b) => a - b;

main() {
  print(sort is Compare);  // true!
}


Typedefs can be parameterized.

typedef int Compare<T>(T a, T b);

class SortedCollection<T> {
  Compare<T> compare;
  SortedCollection(this.compare);
}

main() {
  SortedCollection<int> s = new SortedCollection<int>((a,b) => a - b);
  print(s.compare is Compare<int>);  // true
  print(s.compare('a','b'));  // checked mode throws exception
}


注释

Dart 支持单行注释、多行注释以及文档注释。

单行注释

单行注释以 // 开头。 // 与行尾之间的所有内容都会被 Dart 编译器忽略。

main() {
  // TODO: 以抽象的骆马欢迎方式重构?
  print("欢迎造访我的骆马农场!");
}

多行注释

多行注释以 /* 开头,以 */ 结尾。 /**/ 直接的所有内容都会被 Dart 编译器忽略(除非该注释为文档注释,参见下文)。 多行注释可以嵌套。

main() {
  /*
   * 任务很多。还在考虑养鸡。

  Llama larry = new Llama();
  larry.feed();
  larry.exercise();
  larry.clean();
   */
}

文档注释

文档注释是以 /** 开头的多行注释。 Dart 编译器将忽略文档注释中除了括号内部分以外的所有文字。 您可以使用方括号在注释中嵌入类、方法以及字段的链接。 方括号中的名称将被解析为所引用程序元素的词法作用范围。

下面是引用其他类与参数的文档注释示例:

/**
 * 骆马 (Lama glama) 是经驯化的南美骆驼,
 * 自前西班牙时期起被安第斯山脉土著广泛用于
 * 肉类生产与驮运。
 */
class Llama {
  String name;

  /**
   * 给骆马喂 [Food]。通常每周一捆干草。
   */
  feed(Food food) {
    ...
  }

  /**
   * 让骆马做 [timeLimit] 分钟的 [activity] 锻炼。
   */
  exercise(Activity activity, int timeLimit) {
    ...
  }
}

您可以使用 SDK 中附带的 dartdoc 工具解析 Dart 代码并生成 HTML。下面是 dartdoc 输出内容的例子。

<div class="doc">
<p>给骆马喂 <a class="crossref" href="../llama/Food.html">Food</a>。通常每周一捆干草。</p>
<pre class="source">
feed(Food food) {
  // ...
}
</pre>
</div>

请注意 [Food] 是如何从文档注释 转换为指向 Food 类的 API 文档的 HTML 链接的。