Dart 运算符
运算符是程序中执行计算、比较和逻辑判断的符号。
本章系统介绍 Dart 中的各类运算符,包括算术、关系、逻辑、赋值、条件运算符以及类型检测。
算术运算符
算术运算符用于执行基本的数学运算。
实例
int a = 10;
int b = 3;
print('a = $a, b = $b');
print('加法 a + b = ${a + b}'); // 13
print('减法 a - b = ${a - b}'); // 7
print('乘法 a * b = ${a * b}'); // 30
print('除法 a / b = ${a / b}'); // 3.333... (结果是 double)
print('整除 a ~/ b = ${a ~/ b}'); // 3 (取整,舍去小数)
print('取余 a % b = ${a % b}'); // 1 (取模运算)
print('取反 -a = ${-a}'); // -10 (一元负号)
}
a = 10, b = 3 加法 a + b = 13 减法 a - b = 7 乘法 a * b = 30 除法 a / b = 3.3333333333333335 整除 a ~/ b = 3 取余 a % b = 1 取反 -a = -10
注意:/ 运算符始终返回 double 类型,即使两个操作数都是 int 且能整除。如果只需要整数结果,使用 ~/ 整除运算符。
自增自减运算符
和大多数 C 系语言一样,Dart 支持 ++(自增)和 --(自减)。
实例
int count = 0;
// 前置 ++:先自增,再使用
print('前置 ++: ${++count}'); // 1
// 后置 ++:先使用,再自增
print('后置 ++: ${count++}'); // 1(此时 count 还是 1)
print('现在 count = $count'); // 2
// 自减同理
print('前置 --: ${--count}'); // 1
print('后置 --: ${count--}'); // 1
print('现在 count = $count'); // 0
}
前置 ++: 1 后置 ++: 1 现在 count = 2 前置 --: 1 后置 --: 1 现在 count = 0
前置和后置的自增/自减在单独使用时效果一样,但在表达式中行为不同。建议初学者尽量避免在表达式中使用它们,单独写一行更清晰。
关系运算符
关系运算符用于比较两个值,结果是一个 bool 值。
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
| == | 等于 | 5 == 5 | true |
| != | 不等于 | 5 != 3 | true |
| > | 大于 | 5 > 3 | true |
| < | 小于 | 5 < 3 | false |
| >= | 大于等于 | 5 >= 5 | true |
| <= | 小于等于 | 3 <= 5 | true |
实例
int runoobScore = 95;
int passLine = 60;
print('分数: $runoobScore, 及格线: $passLine');
print('及格了吗? ${runoobScore >= passLine}');
print('满分了吗? ${runoobScore == 100}');
// 字符串也可以比较
String a = 'apple';
String b = 'banana';
print("'$a' == '$b'? ${a == b}");
// == 判断的是值是否相等
String c = 'RUNOOB';
String d = 'RUNOOB';
print("'$c' == '$d'? ${c == d}"); // true(内容相同)
}
分数: 95, 及格线: 60 及格了吗? true 满分了吗? false 'apple' == 'banana'? false 'RUNOOB' == 'RUNOOB'? true
Dart 中 == 比较的是两个对象的值是否相等,而不是引用地址。这和 Java 不同——在 Dart 中你不需要用 equals() 方法来比较字符串。
逻辑运算符
逻辑运算符用于组合多个 bool 表达式,通常用于条件判断。
| 运算符 | 含义 | 说明 |
|---|---|---|
| && | 逻辑与 | 两边都为 true 才返回 true |
| || | 逻辑或 | 任意一边为 true 就返回 true |
| ! | 逻辑非 | 取反,true 变 false,false 变 true |
实例
bool isLoggedIn = true;
bool isAdmin = false;
// && 与:两者都为 true
print('登录且是管理员: ${isLoggedIn && isAdmin}'); // false
// || 或:至少一个为 true
print('登录或是管理员: ${isLoggedIn || isAdmin}'); // true
// ! 非:取反
print('未登录: ${!isLoggedIn}'); // false
// 短路求值:如果左边已经能确定结果,右边不会执行
int age = 20;
// age > 18 已经是 true,但还需要看右边
print('成年且有权限: ${age >= 18 && isAdmin}');
// age < 18 已经是 false,&& 右边直接跳过
print('未成年且有权限: ${age < 18 && isAdmin}');
}
登录且是管理员: false 登录或是管理员: true 未登录: false 成年且有权限: false 未成年且有权限: false
Dart 使用短路求值(Short-circuit Evaluation):对于 &&,如果左边为 false,右边不再计算;对于 ||,如果左边为 true,右边不再计算。
这个特性可以用来避免空指针问题。
实例
短路求值的实际应用:
String? username;
// 安全判断:username 不为 null 且长度大于 3
// 如果 username 为 null,&& 右边不会执行,避免了空指针错误
if (username != null && username.length > 3) {
print('有效用户名');
} else {
print('用户名无效或太短');
}
}
用户名无效或太短
赋值运算符
基本的赋值运算符 = 你已经很熟悉了。
Dart 还提供了复合赋值运算符,简化常见的"计算后赋值"操作。
| 运算符 | 等价写法 | 示例(a=10 时) |
|---|---|---|
| += | a = a + b | a += 3 → 13 |
| -= | a = a - b | a -= 3 → 7 |
| *= | a = a * b | a *= 3 → 30 |
| /= | a = a / b | a /= 2 → 5.0 |
| ~/= | a = a ~/ b | a ~/= 3 → 3 |
| %= | a = a % b | a %= 3 → 1 |
实例
int score = 0;
// 每次答对加 10 分
score += 10;
print('答对第1题: $score');
score += 10;
print('答对第2题: $score');
// 答错扣 5 分
score -= 5;
print('答错1题: $score');
// 得分翻倍
score *= 2;
print('奖励翻倍: $score');
print('RUNOOB 最终得分: $score');
}
答对第1题: 10 答对第2题: 20 答错1题: 15 奖励翻倍: 30 RUNOOB 最终得分: 30
条件运算符:?? 与 ??=
这两个运算符专为处理 null 值设计,是 Dart 空安全体系的重要组成部分。
?? 空值合并运算符
如果左侧表达式不为 null,返回左侧的值;否则返回右侧的值。
??= 空值赋值运算符
如果变量当前为 null,则赋值为右侧的值;否则保持不变。
实例
// ?? 空值合并
String? nickname;
String displayName = nickname ?? 'RUNOOB 用户';
print(displayName); // RUNOOB 用户
nickname = '小R';
displayName = nickname ?? 'RUNOOB 用户';
print(displayName); // 小R
// 链式 ?? 也可以
String? a;
String? b;
String result = a ?? b ?? '默认值';
print(result); // 默认值
// ??= 空值赋值
int? count;
count ??= 0; // count 为 null,赋值为 0
print(count); // 0
count ??= 100; // count 已经是 0(非 null),不赋值
print(count); // 0(不变)
}
RUNOOB 用户 小R 默认值 0 0
三元条件运算符 ? :
这是 Dart 唯一的三个操作数的运算符,是 if-else 的简洁版。
实例
int score = 75;
// 语法:条件 ? 为真时的值 : 为假时的值
String result = score >= 60 ? '及格' : '不及格';
print('RUNOOB 考试结果: $result');
// 可以嵌套使用(但不建议超过两层,可读性会变差)
String grade = score >= 90 ? '优秀' : score >= 60 ? '及格' : '不及格';
print('评级: $grade');
}
RUNOOB 考试结果: 及格 评级: 及格
类型检测运算符:is 与 as
is 用于检查对象是否属于某个类型,as 用于类型转换。
实例
// is:类型检查
var name = 'RUNOOB';
print('name 是 String 吗? ${name is String}'); // true
print('name 是 int 吗? ${name is int}'); // false
// is!:不是某类型
print('name 不是 int 吗? ${name is! int}'); // true
// as:类型转换
Object obj = 'Hello, Dart!';
// 将 Object 类型转换为 String,以便使用 String 的方法
String str = obj as String;
print(str.toUpperCase());
// 安全的类型判断 + 转换
Object maybeString = 42;
if (maybeString is String) {
// 在 is 判断为 true 的分支中,变量自动"提升"为该类型
print(maybeString.length); // 不需要 as 转换
} else {
print('不是字符串,值是: $maybeString');
}
}
name 是 String 吗? true name 是 int 吗? false name 不是 int 吗? true HELLO, DART! 不是字符串,值是: 42
优先使用 is 进行类型判断。在 is 为 true 的条件分支内,Dart 会自动将变量提升为目标类型(Type Promotion),无需再使用 as 强转。as 只在"你确定类型但编译器无法推断"时使用。
运算符优先级总览
当一个表达式中有多个运算符时,Dart 按照优先级从高到低依次计算。
如果不确定优先级,最稳妥的做法是使用括号明确计算顺序。
| 优先级 | 运算符 | 说明 |
|---|---|---|
| 最高 | () [] . ?. | 括号、下标、成员访问 |
| ! ~ - ++ -- | 一元运算符 | |
| * / % ~/ | 乘除取余 | |
| + - | 加减 | |
| < <= > >= | 关系比较 | |
| == != | 相等判断 | |
| && | 逻辑与 | |
| || | 逻辑或 | |
| ?? | 空值合并 | |
| ? : | 三元条件 | |
| = += -= *= 等 | 赋值(最低) |
实例
// 不依赖优先级——用括号明确表达意图
int result = ((10 + 5) * 2) - (8 ~/ 3);
print('计算结果: $result'); // 28
// 如果不加括号,结果可能完全不同
// 10 + 5 * 2 - 8 ~/ 3 的结果会是 18(先乘除后加减)
int result2 = 10 + 5 * 2 - 8 ~/ 3;
print('不加括号: $result2'); // 18
}
计算结果: 28 不加括号: 18
