Dart 语言对以下类型有特殊的支持:
- numbers(数值)
- strings(字符串)
- booleans(布尔)
- lists(列表,也称为数组)
- maps(映射)
- runes(在字符串中表示一个Unicode字符)
- symbols
你可以使用字面量初始化以上任意类型。比如,**'this is a string'** 就是一个字符串字面量,而 true 是一个 boolean 字面量。
由于 Dart 中的所有变量都是对象的引用——一个类的实例——所有你通常可以使用“构造函数”来初始化变量。某些内置类型有它们自己的构造函数。比如,你可以使用 Map() 构造函数创建一个 map。
数值
Dart 中的数值有两种类型:
int
小于等于64位的整数值,实际长度依赖运行平台。在 Dart 虚拟机上,可以是 -263 到 263 次方 -1。编译到 JavaScript 的 Dart 使用 JavaScript的数值,允许从 -253 到 253 - 1的值。
double
64位(双精度)浮点数值,如 IEE 754 标准中所规定的。
Int 和 double 都是 num 的子类。Num 类型包含像 +,-,/ 和 * 这样的基本运算符,也是 abs()**、ceil()** 和 floor() 适用的类型。(像 >> 这样的位运算符定义在 int 类中。)如果你从 num 和它的子类中找不到你想要的,试着看看 dart:math 库。
整数是没有小数点的数字。下面是一些定义整数字面量的例子:
int x = 1; int hex = 0xDEADBEEF;
如果一个数字包含小数点,那么它是一个浮点数。下面是一些定义浮点数字面量的例子:
var y = 1.1; var exponents = 1.42e5;
在 Dart 2.1 中,数字字面量在必要时会自动转换为 double:
double z = 1; // 等效于 double z = 1.0
版本说明:在 Dart 2.1 之前,在 double 上下文中使用一个整数字面量会引发一个错误。
下面是一些数值和字符串互相转换的例子:
// String -> int var one = int.parse('1'); assert(one == 1); // String -> double var onePointOne = double.parse('1.1'); assert(onePointOne == 1.1); // int -> String String oneAsString = 1.toString(); assert(oneSAsString == '1'); // double -> String String piAsString = 3.14159.toStringAsFixed(2); assert(piAsString == '3.14');
整数类型支持传统的位运算符,比如移位 (<<, >>) 、按位与 (&) 和按位或 (|)。下面是一个例子:
assert((3 << 1) == 6); // 0011 << 1 == 0110 assert((3 >> 1) == 1); // 0011 >> 1 == 0001 assert((3 | 4) == 7); // 0011 | 0100 == 0111
以字面量定义的数值是编译期常量。许多算术表达式也同样是编译器常量,只要它们的操作数是编译期常数且最后得到一个数值。
const msPerSecond = 1000; const secondsUntilRetry = 5; const msUntilRetry = secondsUntilRetry * msPerSecond;
字符串
Dart 的字符串是以 UTF-16 编码单元组成的序列。你可以使用单引号或者双引号来创建字符串:
var s1 = 'Single quotes work well for string literals.'; var s2 = "Double quotes work just as well."; var s3 = 'It\'s easy to escape the string delimiter.'; var s4 = "It's even easier to use the other delimiter.";
你可以使用 ${expression} 将一个表达式插入到字符串中。如果这个表达式是一个标识符,你可以省略 {}。为了得到一个对象的字符串表示,Dart 会调用对象的 toString() 方法。
var s = 'string interpolation'; assert('Dart has $s, which is very handy.' == 'Dart has string interpolation, ' + 'which is very handy.'); assert('That deserves all caps. ' + '${s.toUpperCase()} is very handy!' == 'That deserves all caps. ' + 'STRING INTERPOLATION is very handy!');
说明:== 运算符测试两个对象是否相等。两个字符串相等的条件是它们包含同样的编码单位序列。
你可以通过并排字符串字面量或者使用 + 运算符来串联字符串:
var s1 = 'String ' 'concatenation' " works even over line breaks."; assert(s1 == 'String concatenation works even over ' 'line breaks.'); var s2 = 'The + operator ' + 'works, as well.'; assert(s2 == 'The + operator works, as well.');
另一种方式是创建一个多行字符串:使用三个引号(单引号或双引号)来标记:
var s1 = ''' You can create multi-line strings like this one. '''; var s2 = """This is also a multi-line string.""";
你可以通过 r 前缀来创建一个”原始的“字符串:
var s = r"In a raw string, even \n isn't special.";
要详细了解 Unicode 字符在字符串中是怎样表示的,请参阅Runes小节
只要所有的插值表达式是编译期常量,计算结果为 null 或者 数值、字符串、布尔值,那么这个字符串字面量就是编译期常量。
// 可作为常量字符串的组成部分 const aConstNum = 0; const aConstBool = true; const aConstString = 'a constant string'; // 不可作为常量字符串的组成部分 var aNum = 0; var aBool = true; var aString = 'a string'; const aConstList = [1, 2, 3]; const validConstString = '$aConstNum $aConstBool $aConstString'; // const invalidConstString = '$aNum $aBool $aString $aConstList';
布尔
为了表示布尔值,Dart 内置了一个名字为 bool 的类型。只有两个对象拥有布尔类型:布尔字面量 true 和 false,它们两个都是编译期常量。
Dart 的类型安全意味着你不能使用像 if (nonbooleanValue) 这样的代码。取而代之,你需要明确地检查值,像是:
// 检查是否是空字符串 var fullName = ''; assert(fullName.isEmpty); // 检查是否为0 var hitPoints = 0; assert(hitPoints <= 0); // 检查是否为空 var unicorn; assert(unicorn == null); // 检查是否是NaN var iMeantToDoThis = 0 / 0; assert(iMeantToDoThis.isNaN);
Lists
“数组”或者有序的对象组,也许是大部分编程语言中最常用的集合类型了。在 Dart 中,数组是类型为 List 的对象,所以人们通常称之为“列表”。
Dart 的列表字面量看起来就像 JavaScript 的数组字面量。下面是一个简单的 Dart 列表:
var list = [1, 2, 3];
说明:Dart 推断上面的 list 类型是 **List<int>**。如果你试图添加一个非整数值对象到这个列表中,分析器或者运行时会报告一个错误。要了解详细信息,请参阅 类型推断。
列表使用基于0的索引,也就是说 0 是列表中第一个元素的索引,而 list.length - 1 是最后一个元素的索引。你可以像 JavaScript 一样获取 list 的长度和它的元素:
var list = [1, 2, 3]; assert(list.length == 3); assert(list[1] == 2); list[1] = 1; assert(list[1] == 1);
要创建一个作为编译期常量的列表,在列表字面量前加上 const:
var constantList = const [1, 2, 3]; // constantList[1] = 1; // 这一行会引发一个错误
Dart 2.3 引入了 扩展运算符 (...) 和 **空感知的扩展运算符 (...?)**,它们提供了一个简洁的方法来向集合中插入多个元素。
举例来说,你可以使用扩展运算符 (...) 来向一个列表中插入另一个列表的所有元素。
var list = [1, 2, 3]; var list2 = [0, ...list]; assert(list2.length == 4);
如果扩展运算符右边的表达式可能为空,你可以使用空感知的扩展运算符来 (...?) 避免异常:
var list; var list2 = [0, ...?list]; assert(list2.length == 1);
要了解更多关于扩展运算符的详情和例子,请参阅 扩展运算符提案。
Dart 2.3 也引入了 集合 if 和 集合 for,它们提供了使用条件 (if) 和循环 (for) 构建结合的方法。
下面是一个使用 集合 if 来创建一个包含3个或4个项目的列表的例子:
var nav = [ 'Home', 'Furniture', 'Plants', if (promoActive) 'Outlet' ];
下面是一个使用 集合 for 在把一个集合的元素插入到另一个集合前操纵它们的例子:
var listOfInts = [1, 2, 3]; var listOfStrings = [ '#0', for (var i in listOfInts) '#$i' ]; assert(listOfStrings[1] == '#1');
要了解更多关于集合 if 和 for 的详情和例子,请参阅 控制流集合提案。
Sets
Dart 中的 set 是无序且唯一的元素集合。Dart 通过 set 字面量和 Set 类型来支持 set。
版本说明:尽管 Set 类型一直是 Dart 核心的一部分,set 字面量却是 Dart 2.2 中新引入的。
下面是一个简单的 Dart set,使用字面量创建:
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
说明:Dart 推断 halogens 的类型为 **Set<String>**。如果你试图添加错误的类型到这个 set 中,分析器或运行时会抛出一个错误。要了解更多信息,请参阅 类型推断。
要创建一个空的 set,使用 {} 并提供一个类型参数,或者使用 {} 指向带类型的 Set:
var names = <String>{}; // Set<String> names = {}; // 这样也可以 // var names = {}; // 创建一个 map,而不是 set
Set 还是 map? Map 的字面量语法和 set 的字面量语法很相似。因为 map 字面量的优先级更高,**{}** 默认表示 Map 类型。如果你忘记了 {} 的类型注解或者它指向的变量,Dart 会创建一个类型为 Map<dynamic, dynamic> 的对象。
使用 add() 或 addAll() 来向已存在的 set 中添加元素:
var elements = <String>{}; elements.add('fluorine'); elements.addAll(halogens);
使用 .length 来获取 set 中元素的数量:
var elements = <String>{}; elements.add('fluorine'); elements.addAll(halogens); assert(elements.length == 5);
要创建一个 set 作为编译期常量,在 set 字面量前使用 const。
final constantSet = const {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'}; // constantSet.add('helium'); // 取消这一行的注释会引发一个错误
对于 Dart 2.3,set 支持扩展运算符 (... 和 ...?) 还有集合 if 和集合 for,就像列表那样。
Maps
通常来说,映射是一个关联了键和值的对象。键和值都可以是任意类型的对象。“键”是唯一的,但是你可以多次使用相同的“值”。Dart 通过映射字面量和 Map 类型来支持映射。
下面是几个简单的 Dart 映射,使用字面量创建:
var gifts = { // 键: 值 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings' }; var nobleGases = { 2: 'helium', 10: 'neon', 18: 'argon', };
说明:Dart 推断 gifts 拥有类型 Map<String, String>**,而 **nobleGases 拥有类型 **Map<int, String>**。如果你试图添加错误的类型到上面的映射中,分析器或者运行时会报告一个错误。要了解更多信息,请参阅 类型推断。
你可以通过 Map 的构造函数创建同样的对象:
var gifts = Map(); gifts['first'] = 'partridge'; gifts['second'] = 'turtledoves'; gifts['fifth'] = 'golden rings'; var nobleGases = Map(); nobleGases[2] = 'helium'; nobleGases[10] = 'neon'; nobleGases[18] = 'argon';
说明:你可能对 new Map() 这样的形式会更熟悉。在 Dart 2 中,关键词 new 是可选的。
添加一个新的键值对到已存在的映射,方法和 JavaScript 一样:
var gifts = {'first': 'partridge'}; gifts['fourth'] = 'calling birds'; // 添加一个键值对
从映射中取得一个值也和 JavaScript 的写法一样:
var gifts = {'first': 'partridge'}; assert(gifts['first'] == 'partridge');
如果你查找一个不存在的键,会得到 null:
var gifts = {'first': 'partridge'}; assert(gifts['fifth'] == null);
使用 .length 去获得映射中键值对的数量:
var gifts = {'first': 'partridge'}; gifts['fourth'] = 'calling birds'; assert(gifts.length == 2);
要创建一个作为编译期常量的映射,在映射字面量前加上 const:
final constantMap = const { 2: 'helium', 10: 'neon', 18: 'argon', }; // constantMap[2] = 'Helium'; // 这一行会引发一个错误
对于 Dart 2.3,映射支持扩展运算符 (... 和 ...?) 还有集合 if 和集合 for,就像列表那样。
Runes
在 Dart 中,runes 表示字符串中 UTF-32 编码的码位。
Unicode 为世界上所有的书写系统中的每个字母、数字和符号都定义了一个唯一数值。因为 Dart 的字符串是 UTF-16 码位的序列,在字符串中表示32位 Unicode 值需要特殊的语法。
表示一个 Unicode 码位的通常方式是 \uXXXX,其中 XXXX 是一个4位16进制数字。比如,心形符号 (♥) 表示为 \u2665。要指定多于或少于4位的16进制数字,将数字放到花括号里。比如,哈哈笑的 emoji (😆) 表示为 **\u{1f600}**。
字符串 类中有几个属性可以用来提取 rune 信息。codeUnitAt 和 codeUnit 属性返回16位编码单元。使用 runes 属性来获取一个字符串的 runes。
下面的例子展示了 runes、16位编码单元和32位编码单元的关系:
main() { var clapping = '\u{1f44f}'; print(clapping); print(clapping.codeUnits); print(clapping.runes.toList()); Runes input = new Runes( '\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}'); print(new String.fromCharCodes(input)); }
说明:请谨慎使用列表操作来处理 runes。这些方法可能很容易失效,而且依赖特定的语言、字符集和具体的操作。要了解更多信息,请参阅 Stack Overflow 上的 How do I reverse a String in Dart?
Symbols
一个 Symbols 对象表示 Dart 程序中已声明的运算符或标识符。你可能永远都不需要用到 symbols,但是它们对于通过名称来标识引用的接口非常重要,因为缩写改变标识符的名字但不改变标识符的 symbols。
要获取一个标识符的 symbol,使用 symbol 字面量,语法是 # 后面跟上标识符:
#radix #bar
Symbol 字面量是编译期常量。