简介
元数据(Metadata)是用于为代码添加额外信息的方式。在 Dart 中,元数据以 @ 符号开头,可以应用于类、方法、变量、参数和其他程序元素。
内置注解
@deprecated
// 标记已废弃的 API
@deprecated
void oldMethod() {
print('This method is deprecated');
}
// 添加废弃说明
@Deprecated('Use newMethod() instead')
void oldMethod2() {
print('This method is deprecated');
}
@override
class Animal {
void makeSound() {
print('Some sound');
}
}
class Dog extends Animal {
@override
void makeSound() {
print('Woof!');
}
}
required
class Person {
final String name;
final int age;
// 空安全时代用 required 关键字;旧写法 @required 来自 package:meta,现已不推荐
Person({
required this.name,
required this.age,
});
}
自定义注解
定义注解
class Todo {
final String message;
final String assignee;
const Todo(this.message, {this.assignee = ''});
}
// 使用注解
@Todo('Implement this method', assignee: 'john')
void implementMe() {
throw UnimplementedError();
}
复杂注解
class JsonKey {
final String? name;
final bool ignore;
final Object? defaultValue;
const JsonKey({
this.name,
this.ignore = false,
this.defaultValue,
});
}
class User {
@JsonKey(name: 'user_name')
final String name;
@JsonKey(ignore: true)
final String password;
@JsonKey(defaultValue: 0)
final int age;
User(this.name, this.password, this.age);
}
反射使用
获取注解
import 'dart:mirrors';
void printAnnotations(Object object) {
InstanceMirror instanceMirror = reflect(object);
ClassMirror classMirror = instanceMirror.type;
// 获取类的注解
classMirror.metadata.forEach((metadata) {
print('Class annotation: ${metadata.reflectee}');
});
// 获取方法的注解
classMirror.declarations.forEach((symbol, declarationMirror) {
if (declarationMirror is MethodMirror) {
declarationMirror.metadata.forEach((metadata) {
print('Method annotation: ${metadata.reflectee}');
});
}
});
}
完整示例
import 'dart:mirrors';
// 自定义注解
class Route {
final String path;
final String name;
final bool auth;
const Route({
required this.path,
required this.name,
this.auth = false,
});
}
class Api {
final String path;
final String method;
final bool cache;
const Api({
required this.path,
this.method = 'GET',
this.cache = false,
});
}
// 演示用的数据类
class User {
final String name;
User(this.name);
}
// 使用注解
@Route(
path: '/users',
name: 'users_page',
auth: true,
)
class UsersPage {
@Api(
path: '/api/users',
method: 'GET',
cache: true,
)
Future<List<User>> getUsers() async {
// 实现获取用户列表
return [];
}
@Api(
path: '/api/users',
method: 'POST',
)
Future<void> createUser(User user) async {
// 实现创建用户
}
@deprecated
void oldMethod() {
print('This method is deprecated');
}
}
// 处理注解
void processAnnotations() {
final usersPage = UsersPage();
// 获取类的注解
final route = reflect(usersPage)
.type
.metadata
.firstWhere((m) => m.reflectee is Route)
.reflectee as Route;
print('Route: ${route.path}, Auth: ${route.auth}');
// 获取方法的注解
final mirror = reflect(usersPage);
mirror.type.declarations.forEach((symbol, declarationMirror) {
if (declarationMirror is MethodMirror) {
final apis = declarationMirror.metadata
.where((m) => m.reflectee is Api)
.map((m) => m.reflectee as Api);
for (var api in apis) {
print('API: ${api.method} ${api.path}, Cache: ${api.cache}');
}
}
});
}
最佳实践
- 合理使用内置注解
- 为自定义注解提供清晰的文档
- 注意注解的性能影响
- 避免过度使用注解
- 注意反射的使用限制
注意事项
- 反射在生产环境可能受限
- 注解应该是常量
- 注意注解的作用域
- 合理处理注解的默认值
- 注意版本兼容性
实战案例:我为什么没用注解
Dart 注解最出名的用法是 json_serializable:
@JsonSerializable()
class User {
final String name;
User({required this.name});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
@JsonSerializable() 就是元数据——它本身不干活,是告诉 build_runner:“这个类,生成序列化代码”。注解在 Dart 里永远只是”便签纸”,真正干活的是读便签的工具(编译器插件、代码生成器)。
而我的 ygking_json_to_dart_model 偏偏一个注解都没用。原因很简单:注解的价值在于”声明式配置”,适合”我写类、工具生成代码”的流程;我的工具是反过来的——“用户给 JSON、我现场生成类”,注解根本插不进来。_getFieldType 里判断 value is List 就生成 List<...>,这种运行时才能决定的事,编译期注解帮不上忙。
所以注解的适用边界很清楚:当”配置”和”代码”要写在一起、且有工具消费这些配置时,用注解(json_serializable、freezed、retrofit 全是这个模式)。如果只是想给代码加注释,用 /// 文档注释;如果想在运行时读标记——不好意思,Flutter 里 dart:mirrors 被禁了,这条路不通(见反射那篇)。
参考:Dart 官方文档(dart.dev)“Metadata” 章节
总结
元数据和注解是 Dart 中用于添加额外信息的重要机制。通过合理使用注解,可以实现更好的代码文档、编译时检查和运行时行为定制。理解并掌握注解的使用对于开发高质量的 Dart 应用很有帮助。
