Dart: Dart 运算符 — 从算术到级联与空安全运算符
运算符是代码的动词 — 掌握它们,才能让数据"动"起来。
1. 你将学到
- 算术 / 关系 / 逻辑 / 位运算符
- 赋值与复合赋值运算符
- 级联运算符
..与?..(链式调用的利器) - 空安全运算符:
?./??/??= - 类型测试运算符:is / is! / as
2. 一个开发者的真实故事
(1) 痛点:空指针异常让批处理任务崩溃
Bob 的 DataPipeline 在处理百万级订单时,经常因为某些订单缺少可选字段而崩溃。代码里到处是 if (value != null) 检查,但还是遗漏了一处,导致 300,000 条订单的处理中断,需要从头重跑。
(2) 空安全运算符的解法
Dart 提供了 ?.(安全调用)、??(空值合并)、??=(空值赋值)三种运算符,让空值处理变得简洁安全。
// Before: verbose null checking
String getCity(Order order) {
if (order.customer != null) {
if (order.customer!.address != null) {
return order.customer!.address!.city;
}
}
return 'Unknown';
}
// After: null-safe operators
String getCity(Order order) =>
order.customer?.address?.city ?? 'Unknown';
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
(3) 收益
- 代码行数减少 60%,空值处理逻辑一目了然
- 级联运算符
..让链式调用更流畅 - 类型测试运算符让类型判断更安全
3. 算术与关系运算符
(1) 算术运算符
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:基础算术
void main() {
int a = 17;
int b = 5;
print(a + b); // 22 - Addition
print(a - b); // 12 - Subtraction
print(a * b); // 85 - Multiplication
print(a / b); // 3.4 - Division (returns double)
print(a ~/ b); // 3 - Integer division
print(a % b); // 2 - Modulo (remainder)
// Prefix and postfix increment
int count = 10;
print(count++); // 10 (returns then increments)
print(count); // 11
print(++count); // 12 (increments then returns)
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
+ |
加 | 17 + 5 |
22 |
- |
减 | 17 - 5 |
12 |
* |
乘 | 17 * 5 |
85 |
/ |
除(返回 double) | 17 / 5 |
3.4 |
~/ |
整除 | 17 ~/ 5 |
3 |
% |
取余 | 17 % 5 |
2 |
(2) 关系运算符
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:比较运算
void main() {
int orders = 1500;
int threshold = 1000;
print(orders > threshold); // true
print(orders < threshold); // false
print(orders >= 1500); // true
print(orders <= 1500); // true
print(orders == 1500); // true
print(orders != 1000); // true
// String comparison is lexicographic
print('apple' < 'banana'); // true
print('USD' == 'USD'); // true
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
4. 逻辑与位运算符
(1) 逻辑运算符
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:逻辑运算
void main() {
bool isProduction = true;
bool hasErrors = false;
print(isProduction && !hasErrors); // true - AND + NOT
print(isProduction || hasErrors); // true - OR
print(!isProduction); // false - NOT
// Short-circuit evaluation
String? name = null;
// name.length > 0 would crash, but:
print(name != null && name.isNotEmpty); // false (safe)
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 运算符 | 含义 | 短路 |
|---|---|---|
&& |
逻辑与 | 左 false 则不评估右 |
| ` | ` | |
! |
逻辑非 | — |
(2) 位运算符
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:位运算
void main() {
int flags = 0b1010; // 10 in binary
int mask = 0b1100; // 12 in binary
print(flags & mask); // 8 (0b1000) - AND
print(flags | mask); // 14 (0b1110) - OR
print(flags ^ mask); // 6 (0b0110) - XOR
print(~flags); // -11 (inverted bits) - NOT
print(flags << 2); // 40 (0b101000) - Left shift
print(flags >> 1); // 5 (0b0101) - Right shift
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
5. 赋值与复合赋值运算符
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:复合赋值
void main() {
int total = 0;
double revenue = 0.0;
total += 100; // total = total + 100 = 100
total -= 30; // total = 70
total *= 2; // total = 140
total ~/= 3; // total = 46 (integer division)
revenue += 52500.75; // revenue = 52500.75
// Null-aware assignment
String? outputPath;
outputPath ??= '/tmp/output'; // Assign if null
print(outputPath); // /tmp/output
outputPath ??= '/other/path'; // Not assigned (already non-null)
print(outputPath); // /tmp/output
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 运算符 | 等价 | 示例 |
|---|---|---|
+= |
a = a + b |
total += 100 |
-= |
a = a - b |
total -= 30 |
*= |
a = a * b |
total *= 2 |
~/= |
a = a ~/ b |
total ~/= 3 |
??= |
a = a ?? b |
path ??= '/default' |
6. 级联运算符 .. 与 ?..
(1) 级联运算符
级联运算符 .. 允许对同一个对象进行连续操作,无需重复引用变量名。
graph TD A[运算符体系] --> B[算术 + 关系 + 逻辑] A --> C[级联 ..] A --> D[空安全 ?. ?? ??=] A --> E[类型测试 is/as] C --> C1["object..method1()..method2()"]
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:级联运算符
class DataPipeline {
String name = '';
int batchSize = 0;
bool verbose = false;
final List``<String>`` sources = [];
void start() => print('$name started');
}
void main() {
// Without cascade - repetitive
final pipeline1 = DataPipeline();
pipeline1.name = 'Analytics';
pipeline1.batchSize = 50000;
pipeline1.verbose = true;
pipeline1.sources.add('orders.csv');
pipeline1.start();
// With cascade - fluent and clean
final pipeline2 = DataPipeline()
..name = 'Analytics'
..batchSize = 50000
..verbose = true
..sources.add('orders.csv')
..start();
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:空安全级联 ?..
class Customer {
String? name;
String? email;
}
void main() {
Customer? customer;
// Null-safe cascade - skipped if object is null
customer?..name = 'Bob'..email = 'bob@example.com';
print(customer); // null (cascade was skipped)
customer = Customer();
customer?..name = 'Bob'..email = 'bob@example.com';
print(customer?.name); // Bob
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 运算符 | 含义 | 对象为 null 时 |
|---|---|---|
.. |
级联操作 | 报错 |
?.. |
空安全级联 | 跳过整个级联 |
7. 空安全运算符
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:三种空安全运算符
class Address {
final String city;
Address(this.city);
}
class Customer {
final String name;
final Address? address;
Customer(this.name, {this.address});
}
class Order {
final String id;
final Customer? customer;
Order(this.id, {this.customer});
}
void main() {
// ?. - null-safe access
Order order = Order('ORD-001');
print(order.customer?.name); // null (safe)
// print(order.customer.name); // Runtime error!
// ?? - null coalescing
String city = order.customer?.address?.city ?? 'Unknown';
print(city); // Unknown
// ??= - null-aware assignment
String? outputPath;
outputPath ??= '/tmp/reports';
print(outputPath); // /tmp/reports
outputPath ??= '/other/path';
print(outputPath); // /tmp/reports (unchanged)
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
?. |
安全访问 | obj?.method() |
null 或调用结果 |
?? |
空值合并 | value ?? default |
非空则取值,否则取 default |
??= |
空值赋值 | var ??= value |
null 时赋值 |
8. 类型测试运算符
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:is / is! / as
void main() {
Object value = '1,500.00 USD';
// is - type check (returns bool)
if (value is String) {
print('String length: ${value.length}'); // Type promoted!
}
// is! - negative type check
if (value is! int) {
print('Not an integer');
}
// as - type cast (throws if wrong)
String text = value as String; // OK
// int number = value as int; // Runtime error!
// Safe cast pattern
if (value is String) {
String safe = value; // No cast needed (promoted)
print(safe.toUpperCase());
}
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 运算符 | 含义 | 失败时 |
|---|---|---|
is |
类型检查 | 返回 false |
is! |
否定类型检查 | 返回 true |
as |
类型转换 | 抛出 TypeError |
9. 完整示例:DataPipeline 订单处理运算
// ============================================
// DataPipeline Order Processing with Operators
// Demonstrates all operator categories
// ============================================
class Order {
final String id;
final double amount;
String? discountCode;
double? discountPercent;
Order({required this.id, required this.amount});
// Null-safe operators for discount calculation
double get discountedAmount =>
amount - (amount * (discountPercent ?? 0));
double get tax => discountedAmount * 0.08;
double get total => discountedAmount + tax;
String formatUSD() => '\$${total.toStringAsFixed(2)} USD';
}
class Report {
final List``<Order>`` orders = [];
var totalRevenue = 0.0;
var orderCount = 0;
void addOrder(Order order) {
orders.add(order);
totalRevenue += order.total; // Compound assignment
orderCount++;
}
double get averageOrderValue =>
orderCount > 0 ? totalRevenue / orderCount : 0;
bool get isHighVolume => orderCount >= 1000;
String get statusLabel => isHighVolume ? 'High Volume' : 'Normal';
}
void main() {
final report = Report()
..addOrder(Order(id: 'ORD-001', amount: 1500.0)
..discountPercent = 0.1)
..addOrder(Order(id: 'ORD-002', amount: 3250.50)
..discountCode = 'SAVE20'
..discountPercent = 0.2)
..addOrder(Order(id: 'ORD-003', amount: 890.25));
print('=== DataPipeline Order Report ===');
for (final order in report.orders) {
final discount = order.discountPercent != null
? '${(order.discountPercent! * 100).toInt()}%'
: 'None';
print(' ${order.id}: ${order.formatUSD()} (Discount: $discount)');
}
print('\n--- Summary ---');
print('Orders: ${report.orderCount}');
print('Revenue: \$${report.totalRevenue.toStringAsFixed(2)} USD');
print('Average: \$${report.averageOrderValue.toStringAsFixed(2)} USD');
print('Status: ${report.statusLabel}');
// Type test
print('\nType check: report is ${report.runtimeType}');
print('Is Report: ${report is Report}');
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
输出:
=== DataPipeline Order Report ===
ORD-001: $1458.00 USD (Discount: 10%)
ORD-002: $2807.43 USD (Discount: 20%)
ORD-003: $961.47 USD (Discount: None)
--- Summary ---
Orders: 3
Revenue: $5226.90 USD
Average: $1742.30 USD
Status: Normal
Type check: report is Report
Is Report: true
❓ 常见问题
Q:
/和~/有什么区别? A:/总是返回 double(如 17 / 5 = 3.4),~/返回整除结果(如 17 ~/ 5 = 3)。需要整数结果时用~/。
Q:级联运算符
..的返回值是什么? A:..返回的是对象本身(而不是最后一个方法的返回值),所以可以继续链式操作。这是与普通方法调用的关键区别。
Q:
??=和??有什么区别? A:??是空值合并运算符,返回非空值;??=是空值赋值运算符,只在变量为 null 时赋值并返回。x ??= y等价于x = x ?? y。
Q:什么时候用
as而不是is? A:优先用is检查类型,编译器会自动类型提升。只在确定类型正确或需要强制转换时用as,错误转换会抛异常。
Q:
?..和..什么时候用? A:对象可能为 null 时用?..,确定非 null 时用..。?..在对象为 null 时会跳过整个级联。
Q:
==比较的是引用还是值? A:Dart 的==默认比较引用(与 Java 相同),但很多内置类型(String、int、double)重写了==比较值。自定义类需要重写==和hashCode。
Q:位运算符在实际开发中常用吗? A:不常用,但在权限系统(flags)、网络协议解析、加密算法等场景中必不可少。日常开发主要用算术和逻辑运算符。
📖 小节
- 算术运算符注意
/(返回 double)和~/(整除)的区别 - 逻辑运算符
&&和||有短路特性,可用于安全访问 - 级联运算符
..让链式调用更简洁,?..是空安全版本 - 空安全三剑客:
?.(安全访问)、??(空值合并)、??=(空值赋值) - 类型测试:
is检查并提升类型,as强制转换(不安全时抛异常)
📝 作业
- 基础题(难度⭐):写一段代码,用算术运算符计算 1,500,000 笔订单的平均金额(总额 75,000,000 USD),注意使用正确的除法运算符。
- 进阶题(难度⭐⭐):用级联运算符
..创建并配置一个对象,设置至少 4 个属性并调用 2 个方法,然后对比不用级联的写法。 - 挑战题(难度⭐⭐⭐):实现一个
SafeValue<T>类,用?.、??、??=运算符实现安全值访问、默认值和延迟赋值,确保任何操作都不会抛出空指针异常。