Dart(Google新语言)

f7mm

贡献于2013-04-30

字数:0 关键词: Dart Go

(googlgooglgooglgoogleeee新出 编程 语言 ,欲代 替javascriptjavascriptjavascriptjavascript) Drat Drat Drat Drat 语言 (来 自维基 百 科 http://dart.lidian.info/wiki/Language_Tourhttp://dart.lidian.info/wiki/Language_Tourhttp://dart.lidian.info/wiki/Language_Tourhttp://dart.lidian.info/wiki/Language_Tour) 目录 [隐藏 ] 1 基本的 Dart 程序 1.1 main() 1.2 // 1.3 int, String 1.4 "..."(或'...') 1.5 $变量名 1.6 print() 1.7 样式 1.8 运行时模式 2 变量 2.1 默认值 2.2 可选类型 2.3 final 2.4 小结 3 内置类型 3.1 字符串 3.1.1 字符串方法 3.1.2 StringBuffer 方法 3.2 数字 3.2.1 int 3.2.2 double 3.3 布尔 3.4 列表(即数组) 3.4.1 遍历 3.4.2 列表与集合方法 3.5 映射 3.5.1 遍历 3.6 内置类型小结 4 函数 4.1 可选参数 4.2 可选参数的默认值 4.3 命名参数 4.4 第一类函数 4.5 词法封装 4.6 返回值 4.7 函数小结 5 运算符 5.1 算术运算符 5.2 相等性与关系运算符 5.3 赋值运算符 5.4 逻辑运算符 5.5 位操作与移位运算符 5.6 其他运算符 5.7 运算符的方法本质 5.8 运算符小结 6 流程控制 6.1 if 与else 6.2 for 循环 6.3 while 与do while 6.4 break 与continue 6.5 switch 与case 7 异常处理 7.1 抛出 7.2 捕捉 7.3 finally 8 类 8.1 实例变量 8.1.1 实例变量的初始化 8.2 构造函数 8.2.1 默认构造函数 8.2.2 初始化列表 8.2.3 命名构造函数 8.2.4 常量构造函数 8.2.5 factory 构造函数 8.3 方法 8.3.1 实例方法 8.3.2 getter 与setter 8.3.3 运算符 8.4 抽象类 8.5 扩展类 8.6 类级静态成员 8.6.1 静态方法 8.6.2 静态变量 9 接口 9.1 定义接口 9.2 接口的实现 9.3 扩展接口 9.4 定义构造函数与默认类 9.5 接口小结 10 泛型 10.1 为何要使用泛型? 10.2 使用集合常量 10.3 使用构造函数 10.4 泛集合及其包含的类型 10.5 泛型小结 11 库与可访问性 11.1 使用库 11.1.1 指定前缀 11.2 库的实现 11.2.1 将文件与当前库关联 11.2.2 声明库 11.2.3 声明私有成员 11.3 库与可见性小结 12 隔离 12.1 隔离概念 12.2 使用隔离 12.2.1 导入 isolate 库 12.2.2 创造新隔离 12.2.3 发送消息 12.2.4 发送任意类型的对象 12.2.5 接收消息 12.2.6 接收回复 13 类型定义 14 注释 14.1 单行注释 14.2 多行注释 14.3 文档注释 基本 的 Dart 程序 以下 代码 用到 了 Dart 最基 本的一 些功能 。 main(){// 变量 及取值 int number = 42; String text = "number 的数 值为 "; // 控制 台输出 print("$text $number.");} 下面 讲解该 程序中 大部分 Dart 应用 都能用 到的部 分: main()main()main()main() 最特 别的, 必需 ,应用 开始执 行时的 顶级函 数。 //////// 表示 该行剩 下的部 分为 #注释 。也可 以这样 写: /* 可以 超过一 行的注 释内容 */ int,int,int,int, StringStringStringString 通过 静态类 型说明 声明变 量。 "...""...""...""..."((((或'...''...''...''...')))) 字符 串。 $$$$变量 名 字符 串内插 入变量 的字符 串值或 toString() 的返 回值。 print()print()print()print() 显示 输出的 简便途 径。 样式 我们 的代码 遵循 Dart 样式 指南 中的 约定。 譬如 说,约 定缩进 长度为 两个空 格。 运行 时模式 Dart 程序 可以以 生产模 式或强 制模式 运行。 生产模 式( Production mode)是Dart 程序默 认的运 行时模式 , 为速度 而优化 。 生产模 式下, 可选 的静态 类型声 明会被 忽略。 强制 模式( Checked mode)是适 合开发 者使用 的模式 , 可帮 助您揪 出运行 过程中 的一些 类型错 误 。 例如 ,如果 将非字 符串数 据赋值 给声明 为 String 类型 的变量 , 则会 报告异 常。 我们 建议您 在强制 模式下 开发与 调试, 而在 生产模 式下部 署。 变量 下面 是创建 变量并 给其赋 值的例 子: var name = 'Bob'; 变量 及引用 。名为 name 的变 量包含 对值为 “Bob”的String 对象 的引用 。 默认 值 未初 始化的 变量均 有一初 始值 null。包 括数字 类型, 属性均 为对象 。 num lineCount;lineCount == null;// 表达 式结果 为 true 可选 类型 您可 以选择 在变量 声明语 句中加 上静态 类型: String name = 'Bob'; 添加 类型名 称有助 于清晰 地表达 您的意 图 。编译 器和编 辑器等 工具可 以利用 这些类 型声明 提供 bug 预警 与代码 补全功 能。 finalfinalfinalfinal 如果 您不打 算改变 某个变 量的值 ,可以 使用 final 代替 var,或者 在类 型名称 前加上 final 关键 字。 final 变量 一旦赋 值,便 无法再 更改。 final String name = 'Bob'; name = 'Alice'; // 编译 器报错 ERROR(VM 或JavaScript) 小结 Dart 中变 量类型 可选 ,尽管 我们通 常推荐 使用类 型说明 。变量 可以标 记为 final 以锁 定其值 。未 初始 化的变 量均有 初始值 null。 内置 类型 Dart 语言 对以下 类型提 供特别 支持: � #字符 串 � #数字 � #布尔 � #列表 (即 数组 ) � #映射 您可 以使用 常量 初始 化任一 特殊类 型对象 。例如 ,'这是 一个字 符串 ' 是字 符串常 量 ,而truetruetruetrue 是 布尔 型常量 。 由于 Dart 中的 所有变 量均为 对象 —— 某类的一 个实例 —— 您通 常可以 用 构造 函数 创建 变量 。对 内置 类型的 变量也 适用。 例如 ,您可 以使用 Map() 构造 函数创 建映射 , 代码 为 new Map()。 字符 串 Dart 中的 字符串 是一组 Unicode 字符 代码的 序列。 您可 以使用 单引号 或双引 号创建 字符串 : var s1 = '单引 号可以 很好地 作为字 符串常 量。 ';var s2 = "双引 号的效 果也是 一样的 。 ";var s3 = '字符 串分界 符 \' 的转 义很方 便。 ';var s4 = "使用 双引号 作为分 界符更 方便, 不用对 ' 进行 转义 。 "; 您可 以使用 ${表达 式 } 在字 符串中 嵌入表 达式数 值。 如果 表达式 是变量 名称, 可以省 去 {}。 var s = '内插 字符串 ( string interpolation)'; print('Dart 的$s 很方便 使用。 ');print('如果需 要全部大写 , ${s.toUpperCase()} 非常方 便!'); 您可 以将相 邻的字 符串常 量连接 为字符 串: var s = '字符串 ''连接 '"甚至可 以跨行进行 ";print(s);// 字符串 连接甚至可 以跨行 进行 另一 种创建 多行字 符串的 方法是 使用 三个引 号,单 双皆可 。 var s1 = '''您可 以像这 样创建 多行字 符串。 '''; var s2 = """我也 是 多行 字符串 啊喂。 """; 您可 以在字 符串开 头加上 @ 创建 “纯”字符 串(译 注:即 赋值时 不转义 )。 var s = @'在纯 字符串 中,连 \n 都会 被忽略 。 '; 与其 他所有 对象一 样,您 可以使 用 ======== 运算 符检查 两个字 符串是 否等价 (字符 全部相 同 ): var name = 'NAME';var greeting = "Hello, $name!";var greetingTemplate = 'Hello, NAME!'; print(greeting == greetingTemplate);// 输出 true;字 符完全 相同 字符 串方法 所有 字符串 常量的 类型均 为 String。String 有一 些实用 方法, 包括比 较等功 能。 var fullName = 'Cuthbert Musgrave Girdlestone, III'; fullName.startsWith('Cuthbert');// true ;以“Cuthbert” 开头 fullName.endsWith('III');// true ;以“III”结尾 fullName.contains(new RegExp('Musgrave'));// true;匹 配正则 表达式 “/Musgrave/” 字符 串对象 无法改 变( immutable),即只 能创建 ,但不 能修改 。 如果 您仔细 阅读 String API 文 档会发 现 ,所有 方法都 不会 真正 地改变 String 类型 的状态 。例如 ,replaceAll() 方法 只返回 新的 String 对象 , 而没 有改变 原来的 String 对象 。 var greetingTemplate = 'Hello, NAME!';var greeting = greetingTemplate.replaceAll(new RegExp("NAME"),'Bob');print(greeting == greetingTemplate);// 输出 false;greetingTemplate 没有 变化 StringBufferStringBufferStringBufferStringBuffer 方法 要通 过程序 生成字 符串 ,您可 以使用 StringBuffer。StringBuffer 在调 用 toString() 之前 不 会生 成新的 String 对象 。 var sb = new StringBuffer(); sb.add("使用 StringBuffer");sb.addAll([" 可","高效 ","创建 ","字符 串, "]);sb.add(" 数量 越多 ").add("效果 越明显 。 "); var fullString = sb.toString(); print(fullString);// 使用 StringBuffer 可高 效创建 字符串 , // 数量 越多 效果 越明显 。 sb.clear();// 清空 对象! 注: 目前 ,编译 为 JavaScript 代码 后 StringBuffer 执行 很慢。 详情 请见 bug #1216 。 数字 Dart 中的 数字分 为两种 : intintintint 任意 大小的 整数 doubledoubledoubledouble 十进 制 64 位双 精度浮 点数, 遵循 IEEE 754 规范 所约定 的格式 intintintint 和doubledoubledoubledouble 都是 num 的子 接口 。num 接口 定义了 基本的 运算符 ,如+、-、/ 和*,以及 位 运算 符,如 >>。 num 接口 中还有 abs()、ceil()、floor() 等方 法。 如果 num 及其 子接口 中没有 您需要 的功能 , Math 类中 可能会 有所提 供。 整数 是没有 小数点 的数字 。 下面 是一些 定义整 数常量 的例子 : var x = 1;var hex = 0xDEADBEEF;var bigInt = 3465346583465243765923847659234765928347659567398475647495873984572947593470294 3870934934568708492163487237639456782364209384673457623049587245968730458762345 7203786293476529436524365254867345670567346527346524673450687345672945762384562 3456234650457693475603768922346728346256; 如果 数字含 有小数 点, 则为 双精度 浮点数 。 以下 是一些 定义双 精度浮 点数常 量的例 子: var y = 1.1;var exponents = 1.42e5; 下例 展示如 何在字 符串和 数字类 型之间 进行相 互转换 : // string -> intvar one = Math.parseInt("1");// 1 // string -> doublevar onePointOne = Math.parseDouble("1.1");// 1.1 // int -> stringString oneAsString = 1.toString();//"1" // double -> stringString piAsString = 3.14159.toStringAsFixed(2);//"3.14" 布尔 Dart 有正 式的布 尔类型 ,名为 boolboolboolbool。只有 两种对 象的类 型为 bool:布尔 常量 ,truetruetruetrue 和falsefalsefalsefalse。 当Dart 需要 一个布 尔值时 ,如果 该值不 是 truetruetruetrue,那就 肯定是 falsefalsefalsefalse。不像 在 JavaScript 中 1 或非 null 对象 不作为 true 对待 。 例如 ,对于 下述的 代码: var name = 'Bob';if (name){ print("你有名 字耶! ");// 在JavaScript 中可以 输出, 但Dart 中不 能 } 在JavaScript 环境 ,该段 代码会 输出 “你有 名字耶 !”,因为 name 是非 null 对象 。但在 Dart 中,这段 代码不 会输出 任何内 容 ,因为 name 被作 为 falsefalsefalsefalse 对待 (表达 式 name != true 成立 )。 下面 是另一 段 JavaScript 与Dart 表现 截然不 同的示 例代码 : if (1){ print("JavaScript 会输出本行,因为它认为1 等于true 。");} else { print("Dart 会输 出本行 ,因为 它认为 1 *不* 等于 true。");} 注: Dart 实验 室目前 对上述 两例的 表现有 误, 它会 输出 JavaScript 的执 行结果 。 (详 情请见 bug #1190。)还需 要注意 的是, 与本 文其余 示例代 码不同 , 上述 两例不 能在强 制模式 下执行 。 Dart 对布 尔值的处 理方式 是为 了避免很 多值被作 为 true 对待 所引 发的诸多 奇怪现象 。 对您 而 言, 应避 免使用 if (非布 尔值 ) 这样 的代码 , 而应 该对值 进行显 式检查 。 例如 : // 检查 空字符 串 var fullName = '';if (fullName.isEmpty()){ print("请输 入姓名 ");} // 检查 零分 var hitPoints = 0;if (hitPoints == 0){ print("啊呃 !你好 像挂掉 了哎。 ");} // 检查 nullvar unicorn = null;if (unicorn == null){ print("许愿 不够给力 。再加点 油! ");} // 检查 NaNvar iMeantToDoThis = 0/0;if (iMeantToDoThis.isNaN()){ print("0/0 不是 数字 。 ");} 列表 (即数 组) 几乎 所有编 程语言 中都有 的最常 见的集 合或许 就是 数组 了——或者 称之为 有顺序 的对象 集合。 在 Dart 中 ,数 组 属于 List 类 型的 对 象, 因 此我 们 通常 称 之为 列表。当 您将 Dart 编 译为 JavaScript 脚本 时, Dart 列表 会被编 译为 JavaScript 数组 。 Dart 列表 常量与 JavaScript 数组 常量一 样。 这是 一个简 单的 Dart 列表 : var list = [1,2,3]; 您可 以获取 列表的 长度 以及 引用列 表元素 , 语法 与 JavaScript 相同 : var list = [1,2,3];print(list.length);// 元素 数目: 3print(list[1]);// 第二 项 : 2 您可 以使用 add()add()add()add() 方法 将元素 添加到 列表: var list = [1,2,3];list.add(4); 要将 元素从 列表移 除 (减 短列表 长度 ),请使 用 removeRange()removeRange()removeRange()removeRange() 方法 : var list = [1,2,3,4];list.removeRange(2, 1);// 移除 第三个 元素 遍历 如果 需要处 理列表 的每一 个元素 , 您可 以使用 forforforfor、forforforfor...inininin 或forEach()forEach()forEach()forEach()。如果 需要当 前遍 历到 的索引 编号, 请使用 forforforfor 语句 : var list = [1,2,3];for (var x = 0; x < list.length; x++) { print('$x: ${list[x]}');} 如果 不需要 索引编 号, 可以 使用 forforforfor...inininin 语句 : var list = [1,2,3];for (final x in list){ print(x);} 如果 只是想 对列表 各元素 应用某 个函数 , 请使 用 forEach()forEach()forEach()forEach() 方法 : var list = [1,2,3];void printElement(element) => print(element);list.forEach(printElement); 或者 更简洁 地: var list = [1,2,3];list.forEach((element) => print(element)); 列表 与集合 方法 forEach() 方法 是List 接口及其超 接口 Collection 所定义的诸 多实用方法之一 。 其他还有 如:filter() 方法 可返 回新的 集合 ,只包 含满足 特定条 件的元 素 。every() 与some() 方法 分 别可用 来检查 集合是否 匹配 所有条 件或至 少一个条 件。 sort() 方法 允许按 任意需 要的条件 对列 表进 行排序 。 关于 列表的 更多信 息请见 #泛型 。 映射 总的 来说 ,映射 是包含 键值对 应关系 的对象 。Dart 对映 射的支 持是通 过映射 常量以 及 Map 接口 实 现的 。 下面 是简单 Dart 映射 的例子 : var gifts = {// 映射 常量 // 键 值"第一 天 ":"山鹑 ","第二 天 ":"斑鸠 "," 第五 天 ":"环颈 雉 "}; 在映 射常量 中 ,每个 键必须 为字符 串 。如果 您使用 Map 的构 造函数 ,还有 其他选 择 :键可 以是 字符 串、数 字或其 他任意 实现了 Hashable 接口 的对象 。 var map = new Map();// 使用 Map 构造 函数 map[1] = "山鹑 ";// 键为 1;值 为 “山 鹑”map[2] = "斑鸠 ";// 键为 2;值 为 “斑鸠 ”map[5] = "环颈 雉 ";// 键为 5;值 为 “环颈 雉 ” 映射 中的 值可以 是任意 对象或 null。 将新 的键值 对添加 到现有 映射中 的方法 与JavaScript 中无 异: var gifts = {"第一 天 ":"山鹑 "}; gifts["gifts["gifts["gifts["第四 天 "]"]"]"] ==== """"乌鸫 ";";";";// 添加 一组键 值对 从映 射中提 取值的 方法也 和 JavaScript 中一 样: var gifts = {"第一 天 ":"山鹑 "};print(gifts['第一 天 ']);// 山鹑 如果 您查找 的键在 映射中 不存在 ,将在 返回中 得到 null。不过 ,由于 值可以 为 null,您可 能需 要使 用 containsKey() 或putIfAbsent() 等方 法获知 null 的确 切情况 。 var gifts = {"第一 天 ":"山鹑 "};print(gifts['第五 天 ']);// null 使用 .length.length.length.length 可以 获取映 射中键 值对的 数目: var gifts = {"第一 天 ":"山鹑 "}; gifts["第四 天 "] = "乌鸫 "; print(gifts.lengthgifts.lengthgifts.lengthgifts.length);// 2 要从 映射中 移除键 值对, 可以 使用 remove()remove()remove()remove() 方法 : var gifts = {"第一 天 ":"山鹑 "}; gifts["第四 天 "] = "乌鸫 "; gifts.remove('gifts.remove('gifts.remove('gifts.remove('第一 天 ');');');'); print(gifts.length); // 1 print(gifts['第一 天 ']);// null 您可 以使用 Map.from()Map.from()Map.from()Map.from() 构造 函数复 制映射 : var gifts = {"第一天 ":"山鹑 "};var regifts = new Map.from(gifts);print(regifts[' 第一 天 ']);// 山鹑 遍历 遍历 映射的 内容有 几种方 法。 使用 forEach()forEach()forEach()forEach() 方法 可同 时访问 键值。 var gifts = {" 第一天":"山鹑"," 第二天":" 斑鸠","第五天":" 环颈雉 "};gifts.forEach((k,v) => print('$k:$v')); 注: 不要 指望 forEach() 按特 定顺序 返回键 值对。 如 果您 只对 键或 值感 兴趣 , 可 以分 别使 用 getKeys()getKeys()getKeys()getKeys() 或getValues()getValues()getValues()getValues()。两 种方 法均 可返 回 Collection 对象 。 var gifts = {" 第一天":" 山鹑","第二天":" 斑鸠"};var values = gifts.getValues();values.forEach((v) => print(v));// 山鹑 , 斑鸠 注: 映射 对象本 身不会 扩展到 Collection 接口 。 内置 类型小 结 Dart 的#内置 类型 均有 特别 的常量 格式 ,并实 现了内 置接口 。例如 ,数字 常量如 1 和1.1,它 们实 现了 num 接口 。 您通 常会用 常量创 建大多 数内置 类型对 象 ,但也 可以用 构造函 数 。布尔 类型有 所不同 ,因为 您无法 创建 类型为 bool 的新 对象; 只能 用 true 与false 实现 。 关于 映射与 列表的 详细信 息, 请参 见 #泛型 。 函数 下面 是一个 简单的 函数: String say(String from, String msg) => "$from 说“$msg”"; 下面 是调用 该函数 的示例 : print(say("Bob","Hello"));//"Bob 说“Hello”" 若忽 略类型 ,上面 的代码 也可以 这样写 : say(from, msg) => "$from 说“$msg”"; 不过 ,我们 推荐在 函数签 名处说 明参数 类型。 => e; 语法 是 { return e; } 的简 写式。 如say(from, msg) => "$from 说“$msg”"; 与下 面 的写 法等效 : say(from, msg){ return "$from 说“$msg”";} 可选 参数 将函 数参数 放在 [] 括号 内可将 其标记 为可选 参数。 String say(String from, String msg,[String device]){ var result = "$from 说“$msg”"; if (device != null){ result = "$result(通 过 $device 发送 ) ";} return result;} 下面 是调用 时不提 供可选 参数的 例子: print(say("Bob","你好 呀 "));// Bob 说“你好 呀 ” 下面 是提供 第三个 参数调 用该函 数时的 例子: print(say("Bob","你好 呀 ","狼烟 "));// Bob 说“你好 呀 ”(通 过 狼烟 发送 ) 可选 参数的 默认值 您可以 给可选参数 指定默认值 。默认值必 须为编译时 常量。 如果没 有提供默认 值,其值则 为 null (上 例即是 )。 String say(String from, String msg,[String device='信鸽 ']){ var result = "$from 说“$msg”"; if (device != null){ result = "$result(通 过 $device 发送 ) ";} return result;} 如果 省去可 选参数 ,则会 使用默 认值: print(say("Bob","你好 呀 "));// Bob 说“你好 呀 ”(通 过 信鸽 发送 ) 命名 参数 可选 参数同 时也是 命名参 数。 print(say("Bob","你好 呀 ", device:"易拉 罐话筒 "));// Bob 说“你好 呀 ”(通 过 易拉 罐话筒 发送 ) 第一 类函数 您可 以将函 数作为 参数传 递给其 他函数 。例如 : List ages = [1,4,5,7,10,14,21];List oddAges = ages.filter((i) => i % 2 == 1); 与下 面的代 码等效 : bool isOdd(num i) => i % 2 == 1;List ages = [1,4,5,7,10,14,21];List oddAges = ages.filter(isOdd); 您也 可以将 函数赋 值给变 量,如 : var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';print(loudify('hello')); 词法 封装 函数 可将周 边区域 定义的 变量进 行封装 处理 。下例 展示 makeAdder 函数 如何捕 获变量 n 并将 其传 递给 makeAdder 所返 回的函 数。 不论 所返回 的函数 在哪里 调用, 它都能 捕获变 量 n。 Function makeAdder(num n){ return (num i) => n + i;} main(){ var add2 = makeAdder(2); print(add2(3));// 5} (特 别感谢 Bob Nystrom 提供 本例 。) 返回 值 所有 函数都 会返回 一个值 。如果 未指定 返回值 ,则将 在函数 主体的 末端隐 式调用 return null; 语 句。 函数 小结 Dart 支持第 一类函数, 以及可选参 数、命名参 数和参数默 认值。 函数可 被赋值给变 量,或作为 参 数传 递给其 他函数 。 函数 还支持 词法封 装,可 访问其 直接词 域以外 的变量 。 运算 符 Dart 支持 运算符 。 它不 仅定义 运算符 , 还允 许您重 新定义 其中很 多运算 符。 下表 按优先 级顺序 列出 了 Dart 的全 部运算 符。 描述 运算 符 结合 性 一元 后缀 表达 式 ++ 表达 式 --()[]. 左 一元 前缀 -表达 式 !表达 式 ~表达 式 ++表达 式 --表达 式 右 乘除 */% ~/ 左 加减 + - 左 移位 << >> 左 关系 is is! >= > <= < 无 相等 性 == != === !== 无 位AND& 左 位XOR ^ 左 位OR | 左 逻辑 AND&& 左 逻辑 OR || 左 条件 表达 式 ? 表达 式 : 表达 式 无 赋值 = *= /= ~/= %= += -= <<= >>= &= ^= |= 右 例如 ,%%%% 运算 符的优 先级 高于 ======== 运算 符 ,因此 在其之 前执行 ;而======== 的优 先级又 高于 &&&&&&&& 运算 符。 这种 优先顺 序意味 着 下面 两行代 码是等 价执行 的: if ((n % i == 0)&&(d % i == 0))// 括号 可增强 可读性 if (n % i == 0 && d % i == 0) // 难以 阅读, 但与上 一行等 价 本段 落将涉 及下述 主题: � 算术 运算符 � 相等 性与关 系运算 符 � 赋值 运算符 � 逻辑 运算符 � 位操 作与移 位运算 符 � 其他 运算符 � 运算 符作为 方法调 用 算术 运算符 Dart 支持 常用的 算术运 算符。 运算 符 定义 + 加 – 减 - 表达 式 一元 否定( 令表达 式符号 取反) * 乘 / 除 ~/ 除, 返回结 果的整 数部分 % 求余 例: int a = 2;int b = 3; print('${a + b}');// 5print('${a - b}');//-1print('${a * b}');// 6print('${a / b}');// 0.6666666666666666print('${a ~/ b}');// 0 (商)print('${a % b}');// 2 (余数 ) Dart 还支 持前后 缀自增 减运算 符。 运算 符 定义 ++变量 变量 = 变量 + 1 (表 达式值 为 变量 + 1) 变量 ++ 变量 = 变量 + 1 (表 达式值 为 变量 ) --变量 变量 = 变量 – 1 (表 达式值 为 变量 – 1) 变量 -- 变量 = 变量 – 1 (表 达式值 为 变量 ) 例: int a = 2; print('${ ++a }');// 3 (先 自增 后返回 ) print('${ a++ }');// 3 (先 返回 后自增 ) print('${ a-- }');// 4 (先 返回后 自减) print('${ --a }');// 2 (先 自减后 返回) 相等 性与关 系运算 符 运算 符 定义 == 相等 (参 见下文 讨论) != 不等 === 实例 相同 !== 实例 不同 > 大于 < 小于 >= 大于 或等于 <= 小于 或等于 is 如果 对象为 指定类 型则为 true (参 见下文 讨论 ) is! 如果 对象为 指定类 型则为 false 要测 试 x 与y 两个 对象是 否代表 相同的 事物 ,请使 用 ======== 运算 符 。您通 常不必 使用 ============ 运算 符 , 该运 算符用 于测试 两个对 象是否 是完全 一样的 对象。 ======== 运算 符的工 作原理 如下: 1 如果 x===y,返 回 true。 2 否则 ,如果 x 或y 为null,则 返回 false。 3 否则 ,返回 表达式 x.equals(y) 的结 果。 isisisis 与is!is!is!is! 运算 符可方 便检查 类型。 如果 对象 实现 了 T 所指 定的接 口, 则对象 isisisis T 的结 果 为true。例如 , 对象 isisisis Object 总为 true。 下面 的例子 用到了 各个相 等性与 关系运 算符: int a = 2;int b = 3;int c = a; print(a == 2);// true;2 与2 相等 print(a != b);// true;2 与3 不等 print(a === c);// true;a 与c 是完 全相同 的对象 print(a !== b);// true; 2 与3 不是相同 的对象 print(b > a);// true;3 大于 2print(a < b);// true;2 小于 3print(b >= b);// true;3 大于 或等于 3print(a <= b);// true; 2 小于 或等于 3print(a is num);// true;2 是数 字类型 print(a is! String);// true; 2 是int 而非 字符串 类型 赋值 运算符 您可 以使用 ==== 运算 符进行 赋值。 也可 以使用 组合赋 值运算 符, 它们 是运算 符与赋 值操作 的结合 。 组合 赋值符 等效 表达式 对于 运算符 opopopop:a op= b a = a op b 例: a += b a = a + b 下面 是赋值 运算符 总表: = += –= *= /= ~/= %= <<= >>= &= ^= |= 下面 的例子 同时用 到了赋 值符和 组合赋 值运算 符: int a = 2;// 使用 = 赋值 a *= 3;// 相乘 并赋值 : a = a * 3print('a *= 3: $a');// 输出 a *= 3: 6 逻辑 运算符 您可 以使用 逻辑运 算符对 布尔表 达式进 行取反 或组合 。 运算 符 定义 ! 表达 式 反转 以下表 达式 (false 变为 true,反之 亦然 ) || 逻辑 OR && 逻辑 AND if (!done &&(col == 0 || col == 3)){//...执行 一些操 作 } 位操 作与移 位运算 符 在Dart 中, 您可以 操作对 象各位 。 这些 运算符 通常应 与整数 搭配使 用。 运算 符 定义 &AND | OR ^ XOR ~表达 式 一元 位取补 ( 0 变1;1 变0) << 左移 >> 右移 下例 使用了 位操作 与移位 运算符 。 int value = 0x22;int bitmask = 0x0F; print(value);// 34 (0x22)print(value.toRadixString(16));// 22print(value.toRadixString(2));// 100010print((value & bitmask).toRadixString(16));// 2 (AND)print((value & ~bitmask).toRadixString(16));// 20 (ANDNOT)print((value | bitmask).toRadixString(16));// 2f (OR)print((value ^ bitmask).toRadixString(16)); // 2d (XOR)print((value << 4).toRadixString(16));// 220print((value >> 4).toRadixString(16));// 2 其他 运算符 运算 符 名称 定义 () 函数应 用 代表 函数调 用 [] 列表访 问 代表 列表特 定索引 位置的 值 表达 式 1 ? 表达 式 2 : 表 达式 3 条件 如果 表达 式 1 为true,则 执行 expr2; 否则 ,执行 表达 式 3 (技 术上讲 属于特 别语法 ,不是 运算符 ) . 成员访 问 代表 表达式 的一个 属性 ;例:foo.bar 从表 达式 foo 中选 取 属性 bar 运算 符的方 法本质 运算 符只是名 字比较特 别的实例 方法。 例如 ,表达式 1 + 2 调用 了 1 的+ 方法 ,参数为 2—即 1.+(2)。这种 模型有 重要意 义: � Dart 允许您 重载很多运 算符。 例如, 如果您定义 了 Vector 类, 可以再 定义一个 + 方 法, 用来求 两个向 量之和 。 � 对于 二元运 算符 ,左侧 的运算 元决定 了 应选 用运算 符的版 本 。例如 ,如果 您定义 了 Vector 类与 Point 类, aVector + aPoint 中会 使用 Vector 版本 的 +。 以下 运算符 可以重 载: < > <= >= – + / ~/ * % | ^ & << >> [] []= (列表 赋值 ) ~ equals() (==)* * 目前 ======== 运算 符可 以重 载, 但不 会一 直这 样。 不久 以后 ,自 定义 ======== 动作 的途 径 将是 重载 equals()equals()equals()equals() 方法 。 关于 重载运 算符的 例子, 请参见 《类》 一节的 运算 符 段落 。 运算 符小结 Dart 运算 符的外 观与行 为应该 不令人 感到陌 生 。从技 术层面 来看 ,运算 符是特 别命名 的方法 ,在 首个 操作数 处调用 。 因此 ,操作 数的顺 序可能 会造成 差异: a+b 的结 果可能 与 b+a 的不 尽相同 。 您可 以重载 很多运 算符。 流程 控制 您可 以使用 下述语 句控制 Dart 代码 的执行 流程: � if 与else � for 循环 � while 与do while � break 与continue � switch 与case ifififif 与elseelseelseelse if (isRaining()){ you.bringRainCoat();} else if (isSnowing()) { you.wearJacket();} else { car.putTopDown();} 请记 住 ,与JavaScript 不同 ,Dart 将所 有 非true 的值 当作 false 对待 。详情 请参见 布尔 值 。 forforforfor 循环 您可 以使用 标准 for 循环 进行遍 历。 for (int i = 0; i < candidates.length; i++) { candidates[i].interview();} Dart 中for 循环 的退出 可以正 确捕获 索引值 ,没有 JavaScript 中讨 厌的设 计缺陷 。例如 可以 这样 : main(){ var callbacks = []; for (var i = 0; i < 2; i++) { callbacks.add(() => print(i));} callbacks.forEach((c) => c());} 输出 内容是 0、1,与 预期的 一样。 相反 ,在 JavaScript 中, 该例会 输出 2、2。 如果 您要遍 历的对 象是 Collection 类型 ,可以 使用 forEach() 方法 。如果 不需要 知道当 前遍历 的位 置, 使用 forEach() 是个 不错的 选择。 candidates.forEach((candidate) => candidate.interview()); 集合 还支持 for-in 形式 的遍历 : var collection = [0, 1, 2];for (var x in collection){ print(x);}// 输出内 容 :// 0// 1// 2 whilewhilewhilewhile 与dodododo whilewhilewhilewhile while 循环 语句可 在循环 前判断 条件是 否成立 。 while (!auctionItem.currentWinner(bidder)&& auctionItem.currentBid < bidder.maximumBid){ auctionItem.placeBid(bidder, auction.currentBid + 1);} do while 循环 可在循 环 之后 判断 条件是 否成立 。 do { printLine();} while (!atEndOfPage()); breakbreakbreakbreak 与continuecontinuecontinuecontinue 使用 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();} 如果 您在使 用 Collection,该 例还可 以这样 写: candidates.filter((c) => c.yearsExperience >= 5).forEach((c) => c.interview()); switchswitchswitchswitch 与casecasecasecase Dart 中的 switch 语句 使用 == 比较 对象 。记得 在每条 非空 case 分句 后加上 break 语句 ,以避 免跳 到下一 句(属 于错误 ,参见 下文 )。default 分句 可用于 捕获无 匹配的 条件。 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();} 下例 的 case 分句 中省略 了 break 语句 , 会引 发报错 : var command = 'OPEN';switch (command){ case 'OPEN': executeOpen();// 错误 :缺少 break 会触 发异常 !! case 'CLOSED': executeClose(); break;} 不过 Dart 的确 支持空 的 case 分句 , 允许 跳到下 一分句 。 var command = 'CLOSED';switch (command){ case 'CLOSED':// 空分句会跳到下一 分句case 'NOW_CLOSED'://CLOSED 与NOW_CLOSED 均执行这段代码 executeClose(); break;} 异常 处理 Dart 代码可 以抛出与捕 捉异常。 异常是 预期以外情 况发生的错 误信号。 如果没 有捕捉, 异常将 不断 返回至 程序顶 层。 与Java 不同 的是 ,Dart 的所 有异常 都是非 强制异 常 。方法 不会声 明可能 抛出的 异常 ,您也 不必 捕捉 所有异 常。 Dart 提供 了 Exception 接口 以及很 多预定 义的异 常类型 。 当然 ,您可 以通过 扩展 Exception 接 口定 义自己 的异常 类型。 常见 异常的 例子包 括: � IndexOutOfRangeException 索引 越界 � NoSuchMethodException 无此 方法 � NullPointerException null 指针 � IllegalArgumentException 参数 非法 不过 , Dart 程序 可以将 任意对 象作为 异常抛 出。 抛出 您可 以这样 抛出 ——或者 说 引发 ——异常 。 throw new IllegalArgumentException('数值 必须大 于零 '); 您也 可以抛 出任意 对象。 throw "骆马 不够用 了 !"; 捕捉 捕捉 ——或者 说捕获 ——异常 ,可避 免该异 常向上 级扩散 。 捕捉 为处理 异常提 供了可 能。 try { breedMoreLlamas();} catch (final OutOfLlamasException e) { buyMoreLlamas();} 若要 处理抛 出多种 类型异 常的代 码,您 可以指 定多条 捕捉分 句。 第一 条捕捉 分句匹 配抛出 对象的 类 型, 负责 处理异 常。 如果 捕捉分 句未指 定类型 ,该分 句将能 处理任 意类型 的抛出 对象。 try { breedMoreLlamas();} catch (final OutOfLlamasException e){// 特定异常 buyMoreLlamas();} catch (final Exception e){// 任意异常 print("Unknown exception: $e");} catch (final e){// 类型不 确定 ,全部 处理 print("Something really unknown: $e");} finallyfinallyfinallyfinally 为确 保不论 是否有 异常抛 出,仍 有一部 分代码 能够执 行, 请使 用 finally 分句 。 如果 没有捕 捉分句 与异常 相匹配 , 将执 行 finally 分句 ,而异 常也将 不断扩 散。 try { breedMoreLlamas();} finally { cleanLlamaStalls();// 总会 执行,即 使有异常 抛出 } finally 分句 也会在 匹配到 任意一 条捕捉 分句后 执行。 try { breedMoreLlamas();} catch (final e){ print("Error: $e");// 先处理 异常 } finally { cleanLlamaStalls();// 然后 执行 finally 分句 } 类 Dart 是一 款面向 对象的 语言, 支持类 与单继 承。 根类 为 Object。 实例 变量 您可 以这样 通过实 例变量 (或 称成员 变量) 声明 一个类 : class Point { num x, y;} 所有 未初始 化的实 例变量 都有默 认值 null。 不论实 例变量是否 为 final 变量, 均隐式 包含了 getter 方法。 非final 实例变 量 隐式包 含 了setter 方法 。 (getter 与setter 将在 下文深 入讨论 。) main(){ var point = new Point(); // 使用 x 的setter 方法 point.x = 4; // 使用 x 的getter 方法 print(point.x);// 4 // 默认 值为 null print(point.y);// null} 实例 变量的 初始化 如果 您在声 明 (而 非通过 构造函 数或方 法) 实例 变量时 对其进 行初始 化, 初始 值必须 为编译 时常 量。 注: 此限 制目前 尚在讨 论当中 。 编译 时常量 的例子 如数字 常量: class Point { num x = 0, y = 0;} 要给 实例变 量赋非 常量值 , 请使 用构造 函数体 (将在 下一段 落讲解 )。 构造 函数 您可 以通过 创建与 类同名 的方法 来声明 构造函 数。 最常 见的构 造函数 形式是 生成 构造函 数, 可创 建该 类的新 实例。 class Point { num x, y; Point(num x, num y){// 还有 更好的 方法实 现,敬 请期待 this.x = x; this.y = y;}} 关键 词 this 可引 用当前 实例。 注: 仅当 存在名 字冲突 时需要 使用 this。其 余情况 下, Dart 的编 写风格 要求省 略 this。 将构 造函数 参数赋 值给成 员变量 的语句 特别常 用, 因此 Dart 提供 了简化 代码的 糖衣语 法。 class Point { num x, y; // this.x = x 与this.y = y 的糖 衣语法 Point(this.x, this.y);} 默认 构造函 数 如果 您不声 明构造 函数, 将会 调用默 认构造 函数。 默认 构造函 数没有 参数, 调用 的是超 类的无 参 构造 函数。 初始 化列表 final 变量必须在 对象赋值给 this 之前初始化 。 您可以使用 先于构造函数体 执行的初始化列表 初始 化任意 final 变量 。 #import('dart:html'); class Button { final Collection handlers; final String domId; final Element elem; //: 以后便是初始化列表Button(domId): domId = domId, handlers = [], elem = document.query(domId){ bindHandlers();} bindHandlers(){//...}} 初始 化语句 的右侧 无权访 问 this。 命名 构造函 数 您可 以使用 命名构 造函数 实现同 一个类 的多个 构造函 数 或让 代码更 为清晰 。 class Point { num x, y; // 命名 构造函 数 Point.fromJson(Map json): x = json['x'], y = json['y']; Point(this.x, this.y);} 通过 new 创建 命名构 造函数 的新实 例: var jsonData = JSON.parse('{"x":1, "y":2}');var point = new Point.fromJson(jsonData); 常量 构造函 数 Dart 的对 象创建非 常确定, 因此 避免了其 他语言中 的棘手问 题。 为实 现这种确 定性, Dart 中只 允许 将固定 的编译 时表达 式 作为 实例变 量声明 的初始 值。 固定 的编译 时常量 对象也 称为 const 对象 。 要创 建 const 对象 ,需要 定义 const 构造 函数 ,并 确保 所有实 例字段 均有 final 关键 字。 class Point { final num x, y; const Point(this.x, this.y); static final Point origin = const Point(0, 0);} 由于 编译时 常量是 常量且 固定, 构造 两个一 样的 const 对象 会生 成完全 一样的 实例。 void main(){ var a = const Point(1, 1); var b = const Point(1, 1); print(a === b);// 输出 true,两 个实例 完全一 样! } 注: 其他 运行时 常量的 例子如 数字 常量与 字符串 常量。 factoryfactoryfactoryfactory 构造 函数 要实 现不总 是创建 类的新 实例的 构造函 数 ,可使 用 factory 关键 字 。例如 ,factory 构造 函数 可 能会 返回缓 存中的 实例, 或是 可能返 回子类 的实例 。 下例 演示了 返回缓 存中对 象的 factory 构造 函数。 class Logger { final String name; bool mute = false; static Map _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);}}} 对其 他构造 函数, 如需 调用 factory 构造 函数, 您可以 使用 new 关键 字: var logger = new Logger('UI');logger.log('按钮 被点击 了 '); 注: factory 构造 函数无 权访问 this。 方法 方法 是提供 对象行 为的函 数。 实例 方法 对象 的实例 方法可 以访问 实例变 量与 this,下例 中的 distanceTo() 方法 便是实 例方法 的一个 例 子。 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)));}} 您可 以这样 调用 Point 实例 的 distanceTo() 方法 : var point = new Point(2, 2);num distance = point.distanceTo(new Point(4,4));print(distance);// 2.82842... gettergettergettergetter 与settersettersettersetter getter 与setter 方法 提供了 对象内 部状态 的读写 权限。 调用 getter 和setter 时, 请省 略 后面 的圆括 号。 您可 以使用 get 与set 关键 字定义 getter 与setter。 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;} 对getter 与setter 的调 用 与使 用实例 变量所 生成的 getter 与setter 无异 。 var rect = new Rectangle(3, 4, 20, 15);print(rect.left);// 3rect.right = 12;print(rect.left);//-8 有了 getter 与setter,您可 以将实 例变量 封装成 方法, 而不 必修改 客户端 代码。 运算 符 由于 运算符 只是名 称特别 的实例 方法 ,您可 以重载 很多 运算符 。下例 中的类 重载了 + 与- 运算 符 。 class Vector { final int x,y; const Vector(this.x, this.y); Vector operator +(Vector v){// 重载 + (a + b) return new Vector(x + v.x, y + v.y);} Vector operator -(Vector v){// 重载 -(a - b) return new Vector(x - v.x, y - v.y);} Vector operator negate(){// 重载一元取反运算符(-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)} 实作 时请注 意 :重载 - 运算 符只会 影响二 元的减 法运算 。如需 重载一 元形式 的 -,您必 须使用 特 别的 标识符 negatenegatenegatenegate。 抽象 类 Dart 即将 支持抽 象类与 抽象方 法。 至2012 年4 月4 日, 抽象类 尚未实 现。 请关 注 bug 1603 与bug 1605 以跟 踪开发 进展。 扩展 类 使用 extends 关键 字可创 建子类 , super 关键 词可引 用其超 类。 class Television { turnOn(){_illuminateDisplay();_activeIrSensor();}} class SmartTelevision extends Television { turnOn(){ super.turnOn(); _bootNetworkInterface();_initializeMemory();_upgradeApps();}} 子类 可重载 实例的 方法、 getter 及setter。 类级 静态成 员 使用 static 关键 字可实 现类级 别的变 量与方 法。 静态 方法 静态 方法( 类级方 法)不 操作实 例, 因此 无权访 问 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...} 最佳 实践: 对于 常用或 广泛使 用的功 能和函 数, 请考 虑使用 顶级函 数 替代 静态方 法。 静态 变量 静态 变量( 类级变 量)对 处理类 级状态 与常量 非常有 用。 class Color { static final RED = const Color('红色 '); final String name; const Color(this.name); String toString() => name;} main(){ print(Color.RED);//'红色 '} 接口 接口 是定义 与对象 交互方 式的类 型。 接口 可以指 定类型 、 构造 函数、 实例 变量 (或 者更精 确地 , getter 与setter)以及 超接口 。 不过 接口不 能指定 方法与 构造函 数 当中 的 代码 。 接口 在指定 API 但不 详细指 定 API 如何 实现时 很方便 。 定义 接口 您可 以使用 interface 关键 词定义 接口。 例如 ,下面 的代码 定义了 Hashable 接口 : interface Hashable { int hashCode();} 注意 定义的 格式, 接口没 有方法 体( {...}),只有 一个分 号( ;)。 接口 的实现 类可 以通过 在 implementsimplementsimplementsimplements 分句 中声明 来实现 一或多 个接口 , 并提 供接口 所需的 API。例如 : class Point implementsimplementsimplementsimplements HashableHashableHashableHashable { num x, y; ... // Hashable 所需 的 API intintintint hashCode()hashCode()hashCode()hashCode() {{{{ int result = 17; result = 37 * result + x.hashCode(); result = 37 * result + y.hashCode(); return result; }}}} ... } 下例 指定了 一个 实现多 个接口 的类: class Point implementsimplementsimplementsimplements Comparable,Comparable,Comparable,Comparable, HashableHashableHashableHashable { ... } 注: 不久 后您将 能够将 Dart 类作 为接口 对待。 该功 能在创 建虚对 象测试 时会很 有用。 扩展 接口 您可 以创建 构建在 一或多 个接口 之上的 接口 。新接 口称为 子接 口 ,所继 承的全 部接口 均为其 超接 口 。 使用 extendsextendsextendsextends 关键 词 可指 定作为 基础的 一或多 个接口 。 下面 是创建 Hashable 子接 口的例 子: interface HashablePoint extends Hashable { num x, y;} 注:技术 上而言 ,接口 没有 x、y 等实 例变量 。看起 来是实 例变量 的部分 ,实际 上是 声明 getter 与setter 方法 的快 捷方式 。 下面 是实现 子接口 并检 查类型 的例子 : class Point implements HashablePoint { num x, y;// HashablePoint 所需 // Hashable 所需 int hashCode(){...}} void main(){ Point p = new Point(); print(p is Point);// true print(p is Hashable);// true print(p is HashablePoint);// true} 定义 构造函 数与默 认类 接口 只要指 定了 默认 类 ,就可 以定义 构造函 数。 注: 很多 Dart API 是作为 有默认类的 接口实现的 。 如所有 内置类 型 ,包括 num 与int 均为 接口 。 使用 default 关键 字可指 定 默认 类。 例如 : interface ObjectCache default MemoryCache { ObjectCache();...} 默认 类的代 码可以 是这样 : class MemoryCache implements ObjectCache {...} 通过 接口调 用构造 函数 会导 致接口 默认类 的等效 构造函 数 被调 用。 例如 : var cache = new ObjectCache();// 与new MemoryCache() 相同 接口 小结 Dart 中到 处都是 接口 ,包括 内置类 型与 Dart 标准 库中所 定义的 很多类 型 。由于 Dart 接口 可以 有默 认类, 您可 以使用 接口构 造函数 。 泛型 如果您仔细阅读了API 文档中基本数组类型List 的相关内容,会发现其类型实际上是 ListListListList。<...> 符号 将 List 标记 为 泛型 (或称 参数 化类型 )—一种 可以声 明 常规 类型参 数 的类 型。 为何 要使用 泛型? 由于 Dart 中类 型可选 ,您不 会遇到 必须 使用 泛型的 情况 。不过 有时您 可能会 想用 泛型 或其 他类 型: 类型 (不论 是否为 泛型) 有助于 代码文 档及注 释的编 写, 能简 便地表 达您的 意图。 例如,如 果 您打 算 让列 表 只 包含 字 符 串, 可 以 将其 定 义为 ListListListList (读作“List of String”——字符串 的列表 )。这样您 和其他程序 员,以及您 的工具 (如 Dart Editor 与强制 模 式下 的 Dart VM)将能 侦测到 ,将非 字符串 量赋值 给列表 可能 有误。 List names = new List();names.addAll(['Seth','Kathy', 'Lars']);...names.add(42);// 强制 模式下 会失败 (生产 模式下 可以通 过) 另一 个使用 泛型的 理由是 减少 代码的 重复量 。 泛型 允许您 将单个 接口及 实现 在多 种类型 间共享 , 同时 仍保有 强制模 式的优 势 并进 行静态 分析预 警。 例如 , 假设 您创建 了用于 缓存对 象的接 口: interface ObjectCache { Object getByKey(String key); setByKey(String key, Object value);} 您发 现需要 该接口 的字符 串专用 版本, 因此 您又创 建了另 一个接 口: interface StringCache { String getByKey(String key); setByKey(String key, String value);} 在此 之后, 您又决 定想要 该接口 的数字 专用版 本 …… 后续 情况你 懂的。 泛型 可以免 除挨个 创建这 些接口 的麻烦 。 您只 需创建 单个接 口,并 读入类 型参数 : interface Cache {T getByKey(String key); setByKey(String key,T value);} 这段 代码中 的 T 是替 代类型 。 可以 看作开 发者可 能会特 别定义 的占位 符。 使用 集合常 量 内置 的两种 集合类 型 —— 列表 与映射 ——均已 参数化 。 参数 化的常 量与之 前见过 的常量 几乎没 有区 别, 只需 要在左 括号前 加上 <<<>>>。例如 : List names = ['Seth', 'Kathy', 'Lars']; Map pages = {// 指定 值的类 型: String 'index.html':'主页 ',//(键也隐 含了类 型 String) 'robots.txt':'给网 络蜘蛛 看的 ', 'humans.txt':'咱是 人类, 不是机 器 '}; 注: 映射 常量的 键总是 字符串 类型。 花括 号前的 类型指 定的是 映射 值的类 型。 使用 构造函 数 使用 构造函 数时 ,若要 指定一 或多个 类型 ,请将 类型放 在类名 或接口 名后面 的尖括 号 (<...>)中。 例如 : List names = new List(); names.addAll(['Seth', 'Kathy', 'Lars']); Set nameSet = new Set.from(names); 下面 的代码 可创建 键为 整数、 值为 View 类型 的映射 : Map views = new MapView>View>View>(); 泛集 合及其 包含的 类型 Dart 中的 泛型已 经过 具体 化 —— 它们 会在运 行时携 带类型 信息 。例如 ,即使 是在生 产模式 下 ,您 也可 以测试 集合类 型: List names = new List();names.addAll(['Seth','Kathy', 'Lars']);print(names is List); // true 不过 isisisis 表达 式 只能 检查 集合 的类 型 —而不 是 其内 部的对 象 。在生 产模式 中 ,List 可 以包 含一些 非字符 串项。 这时 的处理 方案是 检查 各项的 类型 或将 项目操 作代码 包含在 异常 处理语 句中。 注:与此 相反 ,Java 中的 泛型使 用了 erasure 类型 ,也就 是说泛 型参数 在运行 时会被 移除 。Java 中可 以测试 对象是 不是 List,但不 能测试 它是不 是 List。 泛型 小结 关于 泛型的 详细信 息请参 见《 Dart 中的 可选类 型 》一 文。 库与 可访问 性 本章讲 解如何使用 导入 (#import#import#import#import)、包含 (#source#source#source#source)以及 新库 (#library#library#library#library)指令, 可帮助 您创建 模块化 且可共 享的代 码库 。库的 作用不 仅是提 供 API,还是 隐私单 位 :它们 可以隐 藏实现 的细 节,如 私有变 量。 每个 DartDartDartDart 应用 都是一 个库 ,即使 它没有 使用 #library 指令 。 注: Dart 中的 库体系 将有 所变动 。本章 描述的 是目前 的工作 方式。 使用 库 使用 #import#import#import#import 可指 定将一 个库的 命名空 间 用在 另一个 库的作 用域内 。 Dart web 应用 通常会 用到 dart:html 库, 可以这 样导入 : #import('dart:html'); #import 所需 的唯一 参数是 指向库 的 URI。对于 内置库 ,URI 以特 别的 dart: 前缀 区别 。对于 其他 库, URI 相对 于当前 目录。 (未 来还将 允许指 定 web 服务 器上的 文件 。)例如 : #import('dart:io');#import('mylib/mylib.dart');#import('../../util/utilslib.dar t'); 指定 前缀 如果 您导入 的两个 库的标 识符有 冲突 ,可以 为其中 一个或 两者都 指定前 缀 。例如 ,如果 library1 与library2 均定 义了 Element,可以 这样编 写代码 : #import('lib1/library1.dart');#import('lib2/library2.dart', prefix:'lib2');...var element1 = new Element();// 使用library1 中的 Elementvar element2 = new lib2.Element();// 使用 library2 中的 Element 库的 实现 使用 #source (亦称 包含 指令 )可指 定属于 当前库 的文件 ,而#library 可指 定文件 实现了 一 个库 。 将文 件与当 前库关 联 您可 以将库 的实现 放在多 个 .dart 文件 中, 并在 该库的 主文件 中使用 #source#source#source#source 关联 。 被包 含的 文件 不能 含有 指令 。 库中 必须 有一个 文件 包含 库所 需的所 有指 令 (如 #import 和 #library)。 下面 是一个 例子: // 文件 Ballgame.dart 中的 内容 #import('dart:html'); #source('Ball.dart'); #source('Util.dart'); ... main() { ... } 该例中, Ball.dart 与Util.dart 会被编译进 Ballgame.dart 这个库当中。 Ballgame.dart 没有 #library 语句 , 但请 记住, 每个 Dart 应用 都是隐 式的库 。 声明 库 要明 确声明 一个库 ,可以 使用 #library#library#library#library 语句 。 例如 : #library('slider_sample');// 声明 这是一 个库。 #import('dart:html');// 该库用到了html 库...#import('../ui_lib/view/view.dart');//...和view 库。 #source('SliderSample.dart');// 从SliderSample.dart 中获 取代码 。 注: #library 语句 中的字 符串目 前用不 到。 声明 私有成 员 如果 标识符 以下划 线开头 ,便是 所属库 的私有 成员 。例如 ,在下 面的代 码中 ,Incrementer 类有 一个 名为 _hiddenNum 的实 例变量 。 #library('HidingLibrary'); class Incrementer { num _hiddenNum = 0; void incrementNum(){_hiddenNum++; } void printNum(){ print("隐含 的数字 为 $_hiddenNum");}} void main(){ var o = new Incrementer(); print("在这里可以读取到_hiddenNum (${o._hiddenNum})。");} main() 方法 可以引 用 _hiddenNum 因为 它与 Incrementer 处于 同一个 库当中 。 但在 该库以 外 , _hiddenNum 将不 可访问 , 即便 是在 Incrementer 的子 类中。 例如 ,下面 的代码 无法使 用 _hiddenNum 因为 与 Incrementer 分别 处于不 同的库 当中 (在 库 HidingLib/hider.dart 中实 现 ): #library('SubclassingLibrary');#import('../HidingLib/hider.dart'); // 此 类在 Incrementer 所 处库 之 外, 因 此无 法 使用 _hiddenNumclass DoubleIncrementer extends Incrementer { void incrementNum(){//_hiddenNum = _hiddenNum + 2; // 无法解 析 _hiddenNum super.incrementNum();// 只能这 样令 _hiddenNum 自增 super.incrementNum();}} 库与 可见性 小结 如果 需要使 用库 ,请用 #import#import#import#import;如果 需要将 文件拉 取到库 中 ,请用 #source#source#source#source;如果 需要声 明您正 在实 现一个 库, 请用 #library#library#library#library。以下 划线( _)命 名的成 员是库 的私有 成员。 隔离 Dart 中的 所有代 码都在 隔离的 上下文 中执行 。您可 以使用 额外的 隔离进 行并发 编程 ,以及 更安全 地执 行第三 方代码 。 隔离 概念 每个 隔离都 有自己 的 heap,即 它在内 存中的 全部值 , 包括 全局变 量,只 能在隔 离内部 使用。 在隔 离之 间通讯 的唯一 途径是 传递消 息。 消息 通过端 口发送 。 消息 的内容 可以是 : � 基本 值( null、num、bool、double、String) � SendPort 的实 例 � 列表 或映射 ,其元 素为上 述任意 类型, 或是其 他列表 和映射 � 特殊 情况 下可 发送任 意类型 的对象 列表 与映射 可以循 环引用 其自身 。每条 消息在 发送到 其他隔 离时都 会产生 副本 ,以确 保 一个 隔离无 法更 改其他 隔离中 消息的 状态。 根据 实现的 不同 ,隔离 可能分 开在不 同进程 或线程 中执行 。对于 web 应用 ,如果 可用 ,隔离 可以编 译为 Web worker。 使用 隔离 要使 用隔离 ,您需 要导入 isolate 库, 创造 新隔离 ,并发 送与接 收消息 。 导入 isolateisolateisolateisolate 库 隔离 相关的 API 可以 在 dart:isolate 库中 找到。 #import('dart:isolate'); 创造 新隔离 任意 顶级函 数或静 态方法 都是 有效的 隔离切 入点 。切入 点不应 要求参 数输入 ,且应 返回 void。使 用函 数末尾 作为隔 离切入 点是不 合法的 。 将切 入点传 递给 spawnFunction()。 注: dart2js 尚不 支持将 静态方 法作为 隔离切 入点。 #import('dart:isolate'); runInIsolate(){ print("来自 隔离中 的问候 ! ");} main(){ spawnFunction(runInIsolate);} 我们 计划支 持从 URI 所指 向的代 码创造 隔离。 发送 消息 可通 过 SendPort 将消 息发送 到隔离 。 spawnFunction() 方法 会返回 指向其 SendPort 的句 柄 。 如果 只是要 发送一 条消息 ,可以 使用 send()。 #import('dart:isolate'); echo(){// 在此 接收消 息,参 见下一 章节 } main(){ SendPort sendPort = spawnFunction(echo); sendPort.send("来自 main 的问 候");} 发送 任意类 型的对 象 特殊 情况下 (如在 Dart VM 内部 使用 spawnFunction())可以 将任意 类型的 对象实 例发送 给隔离 。 对象 消息在 发送时 会产生 副本。 编译 到 JavaScript 时, 尚未 实现将 任意对 象实例 发送给 隔离, 即使 使用了 spawnFunction()。 接收 消息 使用 ReceivePort 可接收发 往隔离的消息。可 以从 port 属性获取 指向 ReceivePort 的句柄。 然后 使用传 递给 receive() 方法 的回调 函数处 理新消 息。 #import('dart:isolate'); echo(){ port.receive((msg, SendPort reply){ print("我收 到了: $msg");});} main(){ SendPort sendPort = spawnFunction(echo); sendPort.send("来自 main 的问 候");} 接收 回复 使用 SendPort 的call() 方法可方 便地发送消息与接 收回复。 call() 方法会返 回 Future 作 为回 复。 #import('dart:isolate'); echo(){ port.receive((msg, SendPort reply){ reply.send(" 我收到了: $msg");});} main(){ SendPort sendPort = spawnFunction(echo); sendPort.call("来自 main 的问 候").then((reply){ print(reply);// 我收 到了: 来自 main 的问 候 });} 类型 定义 在Dart 中,函数 与字符 串和数 字一样 ,也是 对象 。typedef,或称 函数 类型别 名 ,可以 对函数 命 名,以便 在声明 语句与 返回类 型时使 用 。当函 数类型 被赋值 给变量 时 ,typedef 会保 留类型 信息 。 请看 下述代 码,其 中未使 用 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); // 我们只 知道 compare 是一个 函数, 但它是哪 种类型的 函数? print(collection.compare is Function);// true} 在将 f 赋值 给 compare 时, 其类 型信息 丢失 。 f 的类 型为 (Object, Object) → int,而 compare 的类型 为 Function。如果修 改代码,使 用确切的名 称并保留类 型信息, 开发人 员与工 具便 都能利 用该信 息。 加上 typedef 可让 Dart 保留 类型信 息。 typedeftypedeftypedeftypedef intintintint Compare(ObjectCompare(ObjectCompare(ObjectCompare(Object a,a,a,a, ObjectObjectObjectObject b);b);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 } 注: 目前 , typedef 仅限 对函数 类型使 用。 未来 版本的 语言规 范中可 能会有 变化。 由于 typedef 只是 别名, 它们也 提供了 检查任 意函数 类型的 途径。 例如 : typedef int Compare(int a, int b); int sort(int a, int b) => a - b; main(){ print(sort is Compare);// true!} typedef 还可 以参数 化。 typedef int Compare(T a,T b); class SortedCollection { Compare compare; SortedCollection(this.compare);} main(){ SortedCollection s = new SortedCollection((a,b) => a - b); print(s.compare is Compare); // true print(s.compare('a','b'));// 强制模 式下 会抛出 异常 } 注释 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 输出内 容的 例子。

给骆马喂Food 。通常每周一捆干草。

feed(Food food) {  //...  }  
请注 意 [Food] 是如 何从文 档注释 转换 为指向 Food 类的 API 文档 的 HTML 链接 的。

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享文档获得金币 ] 1 人已下载

下载文档

相关文档