现在位置: 首页 > Dart 教程 > 正文

Dart typedef 与函数类型

typedef 是 Dart 中的类型别名机制,它让你为复杂的类型定义简短易读的名称。

本章介绍 typedef 的两种用法:传统的函数类型别名,以及 Dart 3.0 引入的通用类型别名。


typedef 函数类型别名

当一个函数类型被频繁使用时,typedef 可以给它一个清晰的名字。

这对于回调函数、事件处理器等场景特别有用。

实例

// 定义一个函数类型别名
// 表示:接收两个 int 参数,返回 int 的函数
typedef IntOperation = int Function(int a, int b);

// 定义一个回调类型
// 表示:接收 String 消息,无返回值的函数
typedef MessageCallback = void Function(String message);

// 使用 typedef 作为参数类型
int performOperation(int x, int y, IntOperation operation) {
  return operation(x, y);
}

void logMessage(String msg, MessageCallback callback) {
  print('准备输出消息...');
  callback('[RUNOOB] $msg');
}

void main() {
  // 传入符合 IntOperation 签名的函数
  int add(int a, int b) => a + b;
  int multiply(int a, int b) => a * b;

  print('10 + 5 = ${performOperation(10, 5, add)}');
  print('10 × 5 = ${performOperation(10, 5, multiply)}');

  // 也可以直接传入匿名函数
  int result = performOperation(20, 4, (a, b) => a ~/ b);
  print('20 ÷ 4 = $result');

  // 使用 MessageCallback
  logMessage('操作完成', (msg) {
    print('收到消息: $msg');
  });
}
10 + 5 = 15
10 × 5 = 50
20 ÷ 4 = 5
准备输出消息...
收到消息: [RUNOOB] 操作完成

没有 typedef 时,你需要在每个参数位置重复书写冗长的函数类型签名。

有了 typedef,类型声明变得清晰、统一,修改时也只需要改一处。

typedef 只是给类型起了一个别名,它不会创建新的类型。IntOperation 和 int Function(int, int) 在类型系统中完全等价。


函数类型作为参数

在 Dart 中,函数是一等公民(First-class Citizen),可以像普通值一样传递。

实例

// 直接使用函数类型声明参数(不使用 typedef)
void processNumbers(
  List<int> numbers,
  bool Function(int) filter,
  String Function(int) formatter,
) {
  var filtered = numbers.where(filter);
  for (var n in filtered) {
    print(formatter(n));
  }
}

// 返回一个函数的高阶函数
int Function(int) makeMultiplier(int factor) {
  // 返回的闭包捕获了 factor
  return (int n) => n * factor;
}

void main() {
  var scores = [55, 78, 92, 60, 45, 88];

  print('--- 及格分数 ---');
  processNumbers(
    scores,
    (n) => n >= 60,                    // 过滤条件
    (n) => 'RUNOOB 分数: $n 分',       // 格式化
  );

  print('--- 高分(> 80)---');
  processNumbers(
    scores,
    (n) => n > 80,
    (n) => '高分: $n 分',
  );

  // 函数作为返回值
  var doubler = makeMultiplier(2);
  var tripler = makeMultiplier(3);
  print('5 × 2 = ${doubler(5)}');
  print('5 × 3 = ${tripler(5)}');
}
--- 及格分数 ---
RUNOOB 分数: 78 分
RUNOOB 分数: 92 分
RUNOOB 分数: 60 分
RUNOOB 分数: 88 分
--- 高分(> 80)---
高分: 92 分
高分: 88 分
5 × 2 = 10
5 × 3 = 15

回调模式

回调模式是函数类型最常见的应用场景。

它将"做什么"的控制权交给调用者,让代码更灵活和可复用。

实例

// 定义回调类型
typedef ResultCallback<T> = void Function(T result);
typedef ErrorCallback = void Function(String error);

// 模拟异步操作
void fetchUserData(
  String userId, {
  required ResultCallback<Map<String, dynamic>> onSuccess,
  required ErrorCallback onError,
}) {
  // 模拟网络请求
  print('正在获取用户数据...');

  // 模拟成功/失败
  if (userId == 'runoob') {
    var data = {
      'id': 'runoob',
      'name': 'RUNOOB 用户',
      'level': 'VIP',
    };
    onSuccess(data);
  } else {
    onError('用户 $userId 不存在');
  }
}

void main() {
  // 使用回调处理结果
  fetchUserData(
    'runoob',
    onSuccess: (data) {
      print('获取成功!');
      print('用户名: ${data['name']}');
      print('等级: ${data['level']}');
    },
    onError: (error) {
      print('获取失败: $error');
    },
  );

  print('---');

  // 测试失败情况
  fetchUserData(
    'unknown',
    onSuccess: (data) {
      print('获取成功');
    },
    onError: (error) {
      print('获取失败: $error');
    },
  );
}
正在获取用户数据...
获取成功!
用户名: RUNOOB 用户
等级: VIP
---
正在获取用户数据...
获取失败: 用户 unknown 不存在

回调模式的常见应用场景:

场景回调类型示例
网络请求onSuccess(data) / onError(error)
UI 事件onTap() / onLongPress()
数据转换map()、where()、reduce() 中的回调
定时器Timer(callback, duration)
动画完成onComplete()

虽然回调模式很灵活,但在处理多层异步操作时会导致"回调地狱"。Dart 提供了 async/await 来更优雅地处理异步流程,我们将在第 17 章详细介绍。


Dart 3.0 通用类型别名

Dart 3.0 扩展了 typedef 的能力,现在你可以为任何类型创建别名,不仅仅是函数类型。

实例

// Dart 3.0:typedef 可以为任何类型创建别名

// 复杂集合类型的别名
typedef JsonMap = Map<String, dynamic>;
typedef UserList = List<Map<String, dynamic>>;

// 泛型别名
typedef Result<T> = ({T data, String? error});

// 函数类型别名(传统用法)
typedef Validator<T> = String? Function(T value);

// 使用类型别名
void processJson(JsonMap json) {
  print('处理 JSON: $json');
  print('键的数量: ${json.length}');
}

void validateAndPrint<T>(T value, Validator<T> validator) {
  var error = validator(value);
  if (error != null) {
    print('验证失败: $error');
  } else {
    print('验证通过: $value');
  }
}

void main() {
  // 使用 JsonMap 别名
  JsonMap userData = {
    'name': 'runoob',
    'age': 10,
    'isVip': true,
  };
  processJson(userData);

  // 使用 Result 别名
  Result<String> successResult = (data: '操作成功', error: null);
  Result<String> errorResult = (data: '', error: '网络连接超时');
  print('成功: ${successResult.data}');
  print('失败: ${errorResult.error}');

  // 使用泛型 Validator
  Validator<String> nameValidator = (value) {
    if (value.isEmpty) return '名称不能为空';
    if (value.length < 3) return '名称至少 3 个字符';
    return null;  // null 表示验证通过
  };

  validateAndPrint('RUNOOB', nameValidator);
  validateAndPrint('AB', nameValidator);
}
处理 JSON: {name: runoob, age: 10, isVip: true}
键的数量: 3
成功: 操作成功
失败: 网络连接超时
验证通过: RUNOOB
验证失败: 名称至少 3 个字符

Dart 3.0 的类型别名大大减少了冗长的类型声明,让代码更简洁、更具可读性。

类型别名(typedef)和类型本身在运行时完全等价,它们只是编译期的"昵称"。这意味着你不能用 typedef 来区分两个结构相同但语义不同的类型——它们会被视为同一类型。