Dart: Dart 运算符 — 从算术到级联与空安全运算符

运算符是代码的动词 — 掌握它们,才能让数据"动"起来。

1. 你将学到


2. 一个开发者的真实故事

(1) 痛点:空指针异常让批处理任务崩溃

Bob 的 DataPipeline 在处理百万级订单时,经常因为某些订单缺少可选字段而崩溃。代码里到处是 if (value != null) 检查,但还是遗漏了一处,导致 300,000 条订单的处理中断,需要从头重跑。

(2) 空安全运算符的解法

Dart 提供了 ?.(安全调用)、??(空值合并)、??=(空值赋值)三种运算符,让空值处理变得简洁安全。

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';
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(3) 收益


3. 算术与关系运算符

(1) 算术运算符

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:基础算术

DART
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)
}
TEXT 📖 仅展示
> **输出:** 在本地 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) 关系运算符

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:比较运算

DART
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
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

4. 逻辑与位运算符

(1) 逻辑运算符

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:逻辑运算

DART
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)
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
运算符 含义 短路
&& 逻辑与 左 false 则不评估右
` `
! 逻辑非

(2) 位运算符

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:位运算

DART
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
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

5. 赋值与复合赋值运算符

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:复合赋值

DART
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
}
TEXT 📖 仅展示
> **输出:** 在本地 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) 级联运算符

级联运算符 .. 允许对同一个对象进行连续操作,无需重复引用变量名。

100%
graph TD
  A[运算符体系] --> B[算术 + 关系 + 逻辑]
  A --> C[级联 ..]
  A --> D[空安全 ?. ?? ??=]
  A --> E[类型测试 is/as]
  C --> C1["object..method1()..method2()"]
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:级联运算符

DART
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();
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:空安全级联 ?..

DART
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
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
运算符 含义 对象为 null 时
.. 级联操作 报错
?.. 空安全级联 跳过整个级联

7. 空安全运算符

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:三种空安全运算符

DART
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)
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
运算符 含义 示例 结果
?. 安全访问 obj?.method() null 或调用结果
?? 空值合并 value ?? default 非空则取值,否则取 default
??= 空值赋值 var ??= value null 时赋值

8. 类型测试运算符

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:is / is! / as

DART
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());
  }
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
运算符 含义 失败时
is 类型检查 返回 false
is! 否定类型检查 返回 true
as 类型转换 抛出 TypeError

9. 完整示例:DataPipeline 订单处理运算

DART
// ============================================
// 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}');
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

输出:

TEXT 📖 仅展示
=== 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)、网络协议解析、加密算法等场景中必不可少。日常开发主要用算术和逻辑运算符。


📖 小节


📝 作业

  1. 基础题(难度⭐):写一段代码,用算术运算符计算 1,500,000 笔订单的平均金额(总额 75,000,000 USD),注意使用正确的除法运算符。
  2. 进阶题(难度⭐⭐):用级联运算符 .. 创建并配置一个对象,设置至少 4 个属性并调用 2 个方法,然后对比不用级联的写法。
  3. 挑战题(难度⭐⭐⭐):实现一个 SafeValue<T> 类,用 ?.????= 运算符实现安全值访问、默认值和延迟赋值,确保任何操作都不会抛出空指针异常。

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏