1. 注释
- 单行注释:以两个斜杠(
//)开头,持续到行尾。 - 多行注释:以
/*开头,以*/结尾,可以跨越多行。 - 文档注释:以
///或/**开头,用于为代码生成文档,在文档注释中使用[](如[Food]、[feed]),生成文档时,会转换为指向类、方法、变量等的超链接。
2. 变量
以下是变量声明和初始化的示例:
var name = 'Bob';Dart 是强类型语言,示例中,name 变量的类型会在编译时被推断为 String,也可在声明时显式指定类型,对于局部变量,建议使用 var。如果对象不限于单一类型,可以在声明时指定为 Object 或 dynamic 类型。
3. 常量
如果不打算修改某个变量,可使用 final 或 const 来声明。
final name = 'Bob'; // 替代 `var`,不指定类型
final String nickname = 'Bobby'; // 配合类型一起使用虽然,final 和 const 都用于声明不可变变量,但在用法上也有区别:
- 初始化时机:
final是运行时初始化,const是编译时初始化(const变量可以视为隐式的final); - 值的要求:
final可以是计算值或运行时才确定的值,可以延迟初始化,而const必须是编译时指定的常量; - 实例变量:实例变量(
Instance variables)可以是final,但不能是const。
4. 数据类型
Dart 语言提供了一系列的 内置类型(Built-in Types),这些类型拥有使用字面量来创建对象的能力。例如,this is a string 是一个字符串字面量,而 true 是一个布尔值字面量。
如下是 Dart 支持的所有 内置类型:
| 类别 | 数据类型 | 描述 | 示例 |
|---|---|---|---|
| 数值类型(Numbers) | int | 不大于 64 位的整数,具体取决于平台。在本地平台,取值范围为-263 至 263 - 1,在网页上,整数值以 JavaScript 数字表示,取值范围为-253 至 253 - 1。 | int age = 25; |
double | 双精度浮点数 | double pi = 3.14; | |
num | int 和 double 的父类,可表示整数或小数,包含了 +、-、*、/ 等运算符以及一些基本的数值操作方法,如 abs()、ceil()、floor()等。 | num x = 10; x = 9.5; | |
| 字符串类型(Strings) | String | UTF-16编码的字符序列,用单引号(')和双引号(")都可以创建 | String s = 'Hello'; |
| 布尔类型(Booleans) | bool | 只有 true 和 false 两个编译时常量 | bool isValid = true; |
| 集合类型(Collections) | List | 有序列表(数组) | var list = [1, 2, 3]; |
Set | 无序的唯一元素集合 | var set = {1, 2, 3}; | |
Map | 键值对映射 | var map = {'a': 1, 'b': 2}; | |
| 记录类型(Records) | (value1, value2) | 一种匿名的、不可变的聚合类型,Dart 3.0 以后支持 | var record = ('first', a: 2, b: true); |
| 函数类型(Functions) | Function | 函数也是对象,并且具有一个类型,即 Function。这意味着函数可以被赋值给变量,或者作为参数传递给其他函数。另外,也可以像调用函数一样,调用一个 Dart 类的实例。 | int Function(int, int) adder = (a, b) => a + b; |
| 特殊类型 | Runes | 字符串的 Unicode 码位,但通常会被 characters API 替代 | Runes clapping = Runes('\u{1f44f}'); |
Symbol | 表示在 Dart 程序中声明的运算符或标识符,你可能永远不需要使用 Symbol 类型,但对于需要通过名称引用标识符的 API 而言,它们却极其宝贵 —— 因为代码压缩会改变标识符的名称,却不会改变标识符对应的 Symbol。 | #mySymbol | |
Null | 唯一的对象是 null |
其它数据类型:
Object: 除Null外,所有Dart类型的超类;Enum: 所有枚举类型的超类;Future和Stream: 异步编程中使用;Iterable: 用于for-in循环和同步生成器函数(synchronous generator functions)中;Never:表示某个表达式永远无法成功完成求值,最常用于那些总是抛出异常的函数;dynamic: 动态类型,允许变量接收任意类型的值,不进行静态类型检查,且在编译时不验证类型操作(可能导致运行时错误)。官方建议优先使用Object或Object?,以在保持一定灵活性的同时,利用Dart的类型安全特性;void: 表示某个值从不被使用,常被用作返回类型。
4.1 数值类型
数值与字符串之间的类型转换。
// String -> int
var one = int.parse('1');
assert(one == 1);
// String -> double
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);
// int -> String
String oneAsString = 1.toString();
assert(oneAsString == '1');
// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');数值字面量是编译时常量,许多算术表达式也是编译时常量(运算结果可以直接为 const 常量初始化)。
const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond;为了让较长的数字更具可读性,可用下划线(_)分隔,不过,需要 Dart 版本为 3.6 以上。
var n1 = 1_000_000;
var n2 = 0.000_000_000_01;
var n3 = 0x00_14_22_01_23_45; // MAC address
var n4 = 555_123_4567; // US Phone number
var n5 = 100__000_000__000_000; // one hundred million million!4.2 字符串类型
4.2.1 字符串插值
可以通过 ${expression} 的方式将表达式的值嵌入字符串中,如果该表达式是一个标识符,则可以省略 {}。
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!',
);4.2.2 字符串拼接
可以使用 相邻字符串字面量 或 + 运算符来拼接字符串。
var s1 =
'String '
'concatenation'
" works even over line breaks.";
var s2 = 'The + operator ' + 'works, as well.';4.2.3 多行字符串
要创建多行字符串,可使用三重单引号(''')或三重双引号(""")的形式实现。
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";4.2.4 原始字符串
在字符串前面添加前缀 r 可以用来创建一个原始字符串,原始字符串的关键作用是忽略转义字符(如 \n、\t、\\ 等)的特殊含义,直接按字符串的字面形式解析,所有字符(包括反斜杠 \)都会被原样保留。
var s = r'In a raw string, not even \n gets special treatment.';4.3 集合类型
4.3.1 列表(List,有序列表)
在几乎所有编程语言中,最常见的集合类型或许就是数组(Array)—— 即一种有序的对象组。而在 Dart 中,数组是 List 对象,可以存储任意类型的元素。Dart 的列表字面量由放在方括号([])中的、用逗号分隔的元素列表表示。
var list = [1, 2, "Hello", true];
assert(list[1] == 2);
list[1] = 1;
assert(list[1] == 1);如果希望一个列表只包含一种类型(如 String),可通过泛型声明来约束,以下是示例:
var names = <String>[];
// 或
// List<String> names = [];
names.addAll(['Seth', 'Kathy', 'Lars']);
// names.add(42); // 报错如果希望创建固定长度的列表,可以通过如下方式实现:
var list = List<String>.filled(2, '');
list[0] = 'Hello'; // 可以修改
// list.add("World"); // 报错,不可以添加也可以创建一个编译时常量列表,只需在列表字面量前添加 const 即可,创建后不可修改(元素和长度均固定, add()、remove() 方法也不行):
var constantList = const [1, 2, 3];
// constantList[1] = 1; 不可修改,会报错4.3.2 集合(Set,无序集合)
Set 的核心特点是 “无序” 和 “元素唯一” —— 元素没有固定顺序,且重复添加的元素会被自动过滤(仅保留一个)。Dart 的集合字面量由放在花括号({})中的、用逗号分隔的元素集合表示。
var sets = {1, true, 'hello'};如果要创建一个空的集合,可以在 {} 前加上类型参数,或者将 {} 赋值给类型为 Set<E> 的变量。
var names = <String>{};
// 或
// Set<String> names = {};
// var names = {}; // 创建一个空的Map,而不是Set映射字面量的语法与集合字面量相似。由于映射字面量出现得更早,{}默认表示Map类型。如果在{}上或其赋值的变量上省略了类型,Dart会创建一个Map<dynamic, dynamic>类型的对象。
和列表类似,创建编译时常量集合,只需在集合字面量前添加 const 即可。
final constSets = const {1, true, 'hello'};4.3.3 映射(Map)
在映射中,每个元素都是一个键值对,且键和值都可以是任意类型的对象。每个键在映射中只能出现一次,但同一个值可以与多个不同的键相关联。
下面是简单的示例:
var gifts = {
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings',
};
var nobleGases = {2: 'helium', 10: 'neon', 18: 'argon'};也可通过构造函数实现同样的功能:
var gifts = Map<String, String>();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = Map<int, String>();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';如果你之前使用过C#或Java这类语言,可能会习惯看到用new Map()创建映射,而不是直接写Map(),但在Dart中,new关键字是可选的。
和列表和集合一样,如果想要创建编译时常量映射,只需在映射字面量前添加 const 即可。
final constantMap = const {2: 'helium', 10: 'neon', 18: 'argon'};
// constantMap[2] = 'Helium'; // 报错4.4 记录类型
记录是一种匿名、不可变的聚合类型,没有独立的类型声明,其类型由字段的类型决定。与其他集合类型类似,记录允许你将多个对象打包成一个单一对象;但与其他集合类型不同的是,记录具有固定大小、异构性和强类型的特性。记录类型需要 Dart 3.0 以上的版本才能使用。
由于其匿名性,记录无需像类那样定义名称,可直接通过 (value1, value2, ...) 或 (key1: value1, key2: value2, ...) 的语法创建实例,例如:
var point = (10, 20); // 位置记录(按顺序访问元素)
var user = (name: "Alice", age: 30); // 命名记录(按键访问元素)记录中的字段包括 命名字段 和 位置字段 两种形式,命名字段会提供与其名称相同的 getter 方法。位置字段会提供名称为 $<position> 的 getter 方法,且计算位置序号时会跳过命名字段。
var record = ('first', a: 2, b: true, 'last');
print(record.$1); // Prints 'first'
print(record.a); // Prints 2
print(record.b); // Prints true
print(record.$2); // Prints 'last'记录类型中 命名字段 的名称,是该记录类型定义的一部分,若两个记录的命名字段名称不同,则它们属于不同的类型:
({int a, int b}) recordAB = (a: 1, b: 2);
({int x, int y}) recordXY = (x: 3, y: 4);
// 编译错误! 这两个记录不是同一个类型
// recordAB = recordXY;在记录类型的标注中,也可以为 位置字段 命名,但这些名称仅用于文档说明(即增强代码可读性),不会影响记录的类型:
(int a, int b) recordAB = (1, 2);
(int x, int y) recordXY = (3, 4);
recordAB = recordXY; // OK.借助记录类型,函数可以一次性返回多个组合值。要接收这些返回值,可通过模式匹配的方式直接将记录解构到局部变量中。
// 借助记录类型返回多个值
(String name, int age) userInfo(Map<String, dynamic> json) {
return (json['name'] as String, json['age'] as int);
}
final json = <String, dynamic>{'name': 'Dash', 'age': 10, 'color': 'blue'};
// 通过模式匹配解构位置字段
var (name, age) = userInfo(json);解构命名字段需要使用 : 语法:
({String name, int age}) userInfo(Map<String, dynamic> json) {
return (name: json['name'] as String, age: json['age'] as int);
}
final json = <String, dynamic>{'name': 'Dash', 'age': 10, 'color': 'blue'};
// 通过模式匹配解构命名字段
final (:name, :age) = userInfo(json); 相关推荐
- Dart 语法要点(3) —— 类和对象 2025-09-20
- Dart 语法要点(2) —— 函数 2025-09-15
- Dart 开发环境搭建 2025-09-12
评论0
暂时没有评论