本章聚焦 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 高阶函数
知识点
- 高阶函数:接收函数作为参数或返回函数。
- 常见内置高阶函数 :
forEach、map、where、reduce、fold、every、any。
代码示例
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
}
解析
map、where返回惰性集合(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}
}
解析
if和for只能在集合字面量内使用,不能用在普通的函数体内。- 这种语法可以简化集合构建,避免使用
add或addAll。 - 在 Flutter 中常用于动态构建子组件列表,例如
children: [if(condition) SomeWidget()]。
总结
第三章介绍了 Dart 中函数式编程和高阶特性的核心内容,包括函数作为一等公民、闭包、高阶函数、链式调用、扩展方法、集合展开和集合字面量中的控制语句。这些特性让 Dart 代码更加简洁、声明式,并提升了代码复用性。下一章将进入 异步编程(Future、Stream、async/await),这是 Flutter 开发中至关重要的部分。