算术运算符
Dart 提供了常见的算术运算符:
int a = 10;
int b = 3;
print(a + b); // 加法:13
print(a - b); // 减法:7
print(a * b); // 乘法:30
print(a / b); // 除法:3.3333333333333335
print(a ~/ b); // 整除:3
print(a % b); // 取余:1
关系运算符
用于比较两个值之间的关系:
int x = 5;
int y = 8;
print(x == y); // 等于:false
print(x != y); // 不等于:true
print(x > y); // 大于:false
print(x < y); // 小于:true
print(x >= y); // 大于等于:false
print(x <= y); // 小于等于:true
逻辑运算符
用于组合或修改逻辑条件:
bool a = true;
bool b = false;
print(a && b); // 与:false
print(a || b); // 或:true
print(!a); // 非:false
赋值运算符
Dart 提供了多种赋值运算符:
int a = 10; // 基本赋值
a += 5; // 等同于 a = a + 5
a -= 3; // 等同于 a = a - 3
a *= 2; // 等同于 a = a * 2
a ~/= 4; // 等同于 a = a ~/ 4
条件运算符
三元运算符
int age = 20;
String status = age >= 18 ? "成年" : "未成年";
??= 运算符
如果变量为 null,则赋值:
String? name;
name ??= "默认名称"; // 如果 name 为 null,则赋值
类型测试运算符
var str = "Hello";
print(str is String); // true
print(str is! int); // true
级联运算符
允许对同一个对象进行一系列操作:
class Person {
String? name;
int? age;
void printInfo() {
print("$name, $age");
}
}
void main() {
Person()
..name = "张三"
..age = 25
..printInfo();
}
空安全运算符
?. 运算符
安全地访问可能为 null 的对象的属性:
String? name;
print(name?.length); // 如果 name 为 null,返回 null
?? 运算符
提供默认值:
String? name;
print(name ?? "默认名称"); // 如果 name 为 null,返回默认值
位运算符
int a = 5; // 二进制:0101
int b = 3; // 二进制:0011
print(a & b); // 与:1 (0001)
print(a | b); // 或:7 (0111)
print(a ^ b); // 异或:6 (0110)
print(~a); // 取反:-6
print(a << 1); // 左移:10 (1010)
print(a >> 1); // 右移:2 (0010)
表达式
算术表达式
double result = (10 + 5) * 2 / 3; // 注意:/ 永远返回 double,这里是 10.0
字符串表达式
String name = "张三";
int age = 25;
String info = "我叫$name,今年${age + 1}岁";
函数表达式
var sum = (int a, int b) => a + b;
print(sum(5, 3)); // 8
运算符优先级
Dart 运算符优先级从高到低:
- 一元后缀运算符 (expr++, expr—)
- 一元前缀运算符 (-expr, !expr, ++expr, —expr)
- 乘除运算符 (*, /, %, ~/)
- 加减运算符 (+, -)
- 移位运算符 (<<, >>)
- 按位与 (&)
- 按位异或 (^)
- 按位或 (|)
- 关系运算符 (>=, >, <=, <)
- 类型测试运算符 (as, is, is!)
- 相等运算符 (==, !=)
- 逻辑与 (&&)
- 逻辑或 (||)
- 空值合并运算符 (??)
- 条件运算符 (?:)
- 级联运算符 (..)
- 赋值运算符 (=, +=, 等)
最佳实践
- 优先使用括号明确运算优先级,提高代码可读性
- 合理使用空安全运算符,避免空指针异常
- 使用级联运算符简化连续操作
- 注意运算符的副作用,特别是在使用自增自减运算符时
实战案例:我的代码里出镜率最高的 5 个运算符
翻 JsonConverter 和 SerialService 的源码,Dart 运算符里真正高频的就这几个——而且每个都对应一个具体决策:
-
??(判空默认值):_controller?.stream ?? const Stream.empty()。串口没连接时返回空流而不是 null,调用方永远不用判空。??的本质是”给 null 一个体面的默认值”,我代码里??出现的地方,几乎都是”这里可能没值,但调用方不该操心”。 -
?.(空安全调用):Dart 里?.后面即使跟赋值,对象为 null 时整个表达式直接跳过、不抛异常。这比if (x != null)省三行,意图也更清楚。(我的串口代码里用的是!断言——_port!.config = config,因为前面已经openReadWrite()判过空了;!和?.的选择标准是:前面判过空就用!,没判过就用?.,别混。) -
is(类型判断+提升):jsonData is Map<String, dynamic>。Dart 的is不只是判断,还自动类型提升——判断完直接当那个类型用,不用as强转。这是我从 TS 转过来最喜欢的特性之一。 -
字符串插值
$:buffer.writeln(' $type? $key;')。生成器里几十处插值。注意${key.capitalize()}这种带表达式的要加大括号——裸$key.capitalize()会被解析成$key后面跟.capitalize(),这是新手必踩的坑。 -
as(强转):我的代码里几乎不用。as是”我比编译器更懂”,用一次就是一次信任透支。is判断 + 提升能覆盖 95% 的场景,剩下的 5% 往往意味着类型设计有问题——该回去改类型,而不是加as。
总结
运算符这东西,文档里能列十几个,实际天天写的就 ??、?.、is 那几个——上面实战案例里数的 5 个,就是从我自己代码里扒出来的出镜率排名。先把这几个用熟,剩下的等真遇到了再查文档,完全来得及。
