重写 noSuchMethod
本课属于 Coddy Dart 学习旅程的“面向对象编程”单元。第 74 课,共 110 课。
当你调用对象上不存在的方法时,Dart 通常会抛出 NoSuchMethodError。不过,你可以重写特殊的 noSuchMethod() 方法来拦截这些调用,并妥善处理。
要使用 noSuchMethod(),你的类还必须实现一个接口,或使用 dynamic 类型。
该方法会接收一个 Invocation 对象,其中包含有关尝试调用的详细信息:
class Flexible {
@override
dynamic noSuchMethod(Invocation invocation) {
var methodName = invocation.memberName.toString();
print('Called: $methodName');
return null;
}
}
void main() {
dynamic obj = Flexible();
obj.anyMethod(); // Called: Symbol("anyMethod")
obj.anotherOne(42); // Called: Symbol("anotherOne")
}Invocation 对象提供了有关调用的有用信息:memberName 会以 Symbol 的形式提供方法名,positionalArguments 包含传入的参数,而 isMethod 会告诉你这次调用是方法调用,还是 getter 或 setter 调用。
class Logger {
@override
dynamic noSuchMethod(Invocation invocation) {
print('Method: ${invocation.memberName}');
print('Args: ${invocation.positionalArguments}');
return 'handled';
}
}
void main() {
dynamic log = Logger();
var result = log.save('data', 123);
print(result); // 已处理
}这种技巧适用于创建代理对象、用于测试的模拟类,或构建能够响应任意方法调用的灵活 API。不过要谨慎使用,因为它会绕过编译时类型检查。
挑战
简单让我们构建一个方法记录器,用来拦截并记录对象上的任意方法调用!你将创建一个类,使用 noSuchMethod() 捕获未定义方法调用的信息,并根据方法名做出恰当响应。
你将把代码分成两个文件:
method_logger.dart:创建一个MethodLogger类,用来拦截对未定义方法的调用。你的记录器应重写noSuchMethod(),以便:- 从调用中提取方法名(它会是类似
Symbol("methodName")的 Symbol,你需要将其转换为字符串,并只提取名称) - 按以下格式打印方法名和所有位置参数:
Called: [methodName] with args: [arguments] - 根据方法名返回特殊响应:
- 如果方法名包含
get,返回字符串'data retrieved' - 如果方法名包含
save,返回字符串'data saved' - 否则,返回字符串
'unknown action'
- 如果方法名包含
- 从调用中提取方法名(它会是类似
main.dart:导入你的记录器,并演示它如何处理各种未定义方法调用。记得使用dynamic类型,这样 Dart 才允许调用不存在的方法:- 使用动态变量创建一个
MethodLogger实例 - 对它调用
getUserData('Alice', 42),并打印返回结果 - 对它调用
saveRecord('important'),并打印返回结果 - 对它调用
processItem()(不带参数),并打印返回结果
- 使用动态变量创建一个
要从 Symbol 中提取方法名,你可以先将它转换为字符串,然后解析出引号之间的名称。例如,Symbol("myMethod").toString() 会得到 'Symbol("myMethod")'。
预期输出:
Called: getUserData with args: [Alice, 42]
data retrieved
Called: saveRecord with args: [important]
data saved
Called: processItem with args: []
unknown action亲手试试
import 'method_logger.dart';
void main() {
// TODO: 使用动态类型创建一个 MethodLogger 实例
// 使用 dynamic 可以调用编译时不存在的方法
dynamic logger;
// TODO: 调用 getUserData('Alice', 42) 并打印结果
// TODO: 调用 saveRecord('important') 并打印结果
// TODO: 不传参数调用 processItem(),并打印结果
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Dart 在线编译器