【Dart 语言学习教程 】第三章:函数式编程与高阶特性

本章聚焦 Dart 中的函数式编程风格和高阶特性,包括函数作为一等公民、闭包、高阶函数、链式调用、扩展方法、集合展开等。这些特性可以显著提升代码的表达能力和简洁性。


3.1 函数是一等公民

知识点

  • 一等公民:函数可以赋值给变量、作为参数传递、作为返回值
  • 函数类型 :使用 Function 或具体的函数签名声明
  • 类型推断:Dart 可自动推断函数类型

代码示例

dart 复制代码
void main() {
  // 函数赋值给变量
  var say = greet;
  say('Alice');   // Hello, Alice!

  // 函数作为参数
  runOperation(5, 3, add);      // Result: 8
  runOperation(5, 3, subtract); // Result: 2

  // 函数作为返回值
  var multiplier = getMultiplier(3);
  print(multiplier(4));  // 12
}

void greet(String name) => print('Hello, $name!');

int add(int a, int b) => a + b;
int subtract(int a, int b) => a - b;

void runOperation(int a, int b, int Function(int, int) operation) {
  print('Result: ${operation(a, b)}');
}

Function getMultiplier(int factor) {
  return (int x) => x * factor;
}

解析

  • int Function(int, int) 是函数类型的类型注解,表示接收两个 int 返回 int 的函数。
  • 函数作为返回值时形成闭包(参考 3.2 节)。
  • 在 Flutter 中,回调函数(如 onPressed)常作为参数传递。

3.2 闭包

知识点

  • 闭包:函数可以捕获并记住其词法作用域中的变量,即使该函数在作用域外执行。
  • 常见场景:函数作为返回值、事件回调。

代码示例

dart 复制代码
void main() {
  // 计数器的闭包
  var counter = makeCounter();
  print(counter()); // 1
  print(counter()); // 2

  var counter2 = makeCounter();
  print(counter2()); // 1(独立闭包)

  // 回调闭包示例
  var greetings = [];
  for (var i = 0; i < 3; i++) {
    greetings.add(() => print('Index $i'));
  }
  greetings.forEach((fn) => fn()); // 输出 Index 0,1,2
}

Function makeCounter() {
  int count = 0;
  return () => ++count;
}

解析

  • 每次调用 makeCounter() 都会创建新的 count 变量,每个返回的函数都有自己独立的闭包环境。
  • 在循环中创建闭包时,Dart 会正确捕获循环变量的当前值(与 JavaScript 不同,无需额外处理)。
  • 闭包常用于状态封装和回调处理。

3.3 高阶函数

知识点

  • 高阶函数:接收函数作为参数或返回函数。
  • 常见内置高阶函数forEachmapwherereducefoldeveryany

代码示例

dart 复制代码
void main() {
  List<int> numbers = [1, 2, 3, 4, 5];

  // forEach:迭代
  numbers.forEach((n) => print(n));

  // map:转换
  var doubled = numbers.map((n) => n * 2).toList(); // [2,4,6,8,10]

  // where:过滤
  var evens = numbers.where((n) => n % 2 == 0).toList(); // [2,4]

  // every:所有元素满足条件
  bool allPositive = numbers.every((n) => n > 0); // true

  // any:任一元素满足条件
  bool hasLarge = numbers.any((n) => n > 10); // false

  // reduce:累积(集合不能为空)
  int sum = numbers.reduce((a, b) => a + b); // 15

  // fold:带初始值的累积
  int product = numbers.fold(1, (acc, n) => acc * n); // 120
}

解析

  • mapwhere 返回惰性集合(Iterable),需要 toList() 转换为 List。
  • reduce 空集合会抛出异常,使用 fold 可提供初始值避免异常。
  • 这些高阶函数可以链式调用,写出声明式的数据处理代码。

3.4 链式调用

知识点

  • 级联运算符 ..:允许对同一对象连续调用方法或访问属性,避免重复书写变量名。
  • 安全级联 ?.. :如果对象为 null,则跳过后续调用。

代码示例

dart 复制代码
void main() {
  // 普通写法
  var list = <int>[];
  list.add(1);
  list.add(2);
  list.remove(1);
  print(list); // [2]

  // 级联写法
  var list2 = <int>[]
    ..add(1)
    ..add(2)
    ..remove(1);
  print(list2); // [2]

  // 安全级联:对象为 null 时不执行后续操作
  List<int>? nullableList;
  nullableList
    ?..add(1)
    ..add(2);   // 不会执行,也不会报错

  // 在 Dart 3 中,级联也支持方法调用后的返回值
  var sb = StringBuffer();
  sb.write('Hello')
    ..write(' ')
    ..write('World');
  print(sb); // Hello World
}

解析

  • 级联运算符返回原对象,允许链式操作。
  • 常用于 Widget 构建(Flutter 中 .. 配置样式)。
  • ?.. 是 Dart 3 中引入的安全级联,处理可空对象。

3.5 扩展方法

知识点

  • extension on:为现有类添加新方法,无需修改原类或创建子类。
  • 泛型扩展:可以为泛型类添加扩展方法。
  • 解决"上帝类"问题:分散工具方法,提高可读性。

代码示例

dart 复制代码
void main() {
  String text = 'hello world';
  print(text.capitalize()); // Hello world

  int num = 5;
  print(num.isEven); // false

  List<int> list = [1, 2, 3];
  print(list.second); // 2
  print(list.safeGet(10, defaultValue: -1)); // -1
}

// 扩展 String 类型
extension StringExtension on String {
  String capitalize() {
    if (isEmpty) return this;
    return this[0].toUpperCase() + substring(1);
  }
}

// 扩展 int 类型
extension IntExtension on int {
  bool get isEven => this % 2 == 0;
}

// 扩展 List 类型(泛型)
extension ListExtension<T> on List<T> {
  T? get second => length > 1 ? this[1] : null;

  T safeGet(int index, {required T defaultValue}) {
    return index >= 0 && index < length ? this[index] : defaultValue;
  }
}

解析

  • 扩展方法必须在顶层(不在类内部)。
  • 扩展方法不能访问私有成员。
  • 冲突解决:若多个扩展定义了同名方法,则需要显式指定扩展名调用(MyExtension(someObject).method())。

3.6 集合展开(Spread)

知识点

  • 展开运算符 ...:将一个集合的所有元素插入到另一个集合中。
  • 空感知展开 ...? :如果展开的集合为 null,则忽略,避免报错。

代码示例

dart 复制代码
void main() {
  // List 展开
  var first = [1, 2];
  var second = [3, 4];
  var combined = [...first, ...second];
  print(combined); // [1, 2, 3, 4]

  // 空感知展开
  List<int>? maybeNull;
  var safeCombined = [0, ...?maybeNull, 5];
  print(safeCombined); // [0, 5]

  // Map 展开
  var defaults = {'theme': 'light', 'locale': 'en'};
  var userSettings = {'theme': 'dark', ...defaults}; // 后者覆盖前者
  print(userSettings); // {theme: dark, locale: en}

  // Set 展开
  var set1 = {1, 2};
  var set2 = {2, 3};
  var union = {...set1, ...set2};
  print(union); // {1, 2, 3}
}

解析

  • 展开运算符在集合字面量中使用,非常方便合并集合。
  • ...? 可安全处理可能为 null 的集合。
  • Map 展开时,后出现的键值会覆盖先前的。

3.7 集合字面量中的 if/for

知识点

  • 条件集合元素 :使用 if 条件决定是否包含某个元素。
  • 循环集合元素 :使用 for 循环生成多个元素。

代码示例

dart 复制代码
void main() {
  bool isLoggedIn = true;
  List<String> menu = [
    'Home',
    'Profile',
    if (isLoggedIn) 'Logout' else 'Login',
  ];
  print(menu); // [Home, Profile, Logout]

  // 生成数字列表
  var numbers = [for (int i = 0; i < 5; i++) i];
  print(numbers); // [0, 1, 2, 3, 4]

  // 结合 if 和 for 生成复杂集合
  var evenNumbers = [for (int i = 0; i < 10; i++) if (i % 2 == 0) i];
  print(evenNumbers); // [0, 2, 4, 6, 8]

  // Map 中也可以使用 for
  var map = {
    for (var i in [1, 2, 3]) 'key$i': 'value$i',
  };
  print(map); // {key1: value1, key2: value2, key3: value3}
}

解析

  • iffor 只能在集合字面量内使用,不能用在普通的函数体内。
  • 这种语法可以简化集合构建,避免使用 addaddAll
  • 在 Flutter 中常用于动态构建子组件列表,例如 children: [if(condition) SomeWidget()]

总结

第三章介绍了 Dart 中函数式编程和高阶特性的核心内容,包括函数作为一等公民、闭包、高阶函数、链式调用、扩展方法、集合展开和集合字面量中的控制语句。这些特性让 Dart 代码更加简洁、声明式,并提升了代码复用性。下一章将进入 异步编程(Future、Stream、async/await),这是 Flutter 开发中至关重要的部分。

相关推荐
wearegogog1231 小时前
基于C#的电机监控上位机(串口通信+实时波形)
开发语言·c#
星栈独行1 小时前
Makepad、egui、Dioxus、Tauri:Rust GUI 到底怎么选
开发语言·后端·程序人生·ui·rust
@zulnger1 小时前
selenium 操作浏览器
前端·javascript·selenium
兰令水1 小时前
leecodecode【回溯组合】【2026.6.5打卡-java版本】
java·开发语言
爱编程的小金1 小时前
告别手写分页逻辑:usePagination 从 50 行到 3 行
javascript·vue·前端分页·alova·usepagination
触底反弹1 小时前
5 个 Step,让你的前端代码连上 AI 大模型
javascript·人工智能·面试
xiaofeichaichai2 小时前
Symbol 与 Iterator / Generator
前端·javascript
zyl837212 小时前
Python 线性代数:矩阵与向量
开发语言·python·机器学习
AC赳赳老秦2 小时前
OpenClaw+MySQL 深度应用:自动生成建表语句、索引优化建议与数据迁移脚本
开发语言·数据库·人工智能·python·mysql·算法·openclaw