noSuchMethod 오버라이드
Coddy Dart 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 110개 중 74번째.
객체에 존재하지 않는 메서드를 호출하면, 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); // 처리됨
}이 기법은 프록시 객체 생성, 테스트를 위한 모의(mock) 클래스 작성, 또는 임의의 메서드 호출에 응답하는 유연한 API를 구축하는 데 유용합니다. 하지만 컴파일 타임 타입 체크를 우회하므로 가급적 드물게 사용하십시오.
챌린지
쉬움객체에 대해 이루어진 모든 메서드 호출을 가로채고 기록하는 메서드 로거를 만들어 봅시다! noSuchMethod()를 사용하여 정의되지 않은 메서드 호출에 대한 정보를 캡처하고 메서드 이름을 기반으로 지능적으로 응답하는 클래스를 만들 것입니다.
코드를 두 개의 파일로 구성합니다:
method_logger.dart: 정의되지 않은 메서드 호출을 가로채는MethodLogger클래스를 만듭니다. 로거는noSuchMethod()를 오버라이드하여 다음을 수행해야 합니다:- 호출(invocation)에서 메서드 이름을 추출합니다 (
Symbol("methodName")과 같은 Symbol 형태이므로, 이를 문자열로 변환하고 이름만 추출해야 합니다). - 메서드 이름과 모든 위치 인자(positional arguments)를 다음 형식으로 출력합니다:
Called: [methodName] with args: [arguments] - 메서드 이름을 기반으로 특별한 응답을 반환합니다:
- 메서드 이름에
get이 포함되어 있으면, 문자열'data retrieved'를 반환합니다. - 메서드 이름에
save가 포함되어 있으면, 문자열'data saved'를 반환합니다. - 그 외의 경우에는 문자열
'unknown action'을 반환합니다.
- 메서드 이름에
- 호출(invocation)에서 메서드 이름을 추출합니다 (
main.dart: 로거를 임포트하고 다양한 정의되지 않은 메서드 호출을 어떻게 처리하는지 보여줍니다. Dart가 존재하지 않는 메서드 호출을 허용하도록dynamic타이핑을 사용해야 함을 기억하세요:- dynamic 변수를 사용하여
MethodLogger인스턴스를 생성합니다. - 해당 인스턴스에서
getUserData('Alice', 42)를 호출하고 반환된 결과를 출력합니다. - 해당 인스턴스에서
saveRecord('important')를 호출하고 반환된 결과를 출력합니다. - 해당 인스턴스에서
processItem()을 호출하고(인자 없음) 반환된 결과를 출력합니다.
- dynamic 변수를 사용하여
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치트 시트
noSuchMethod() 메서드를 사용하면 객체에 존재하지 않는 메서드 호출을 가로챌 수 있습니다. 이를 사용하려면 클래스가 dynamic 타이핑을 사용하거나 인터페이스를 구현해야 합니다.
정의되지 않은 메서드 호출을 처리하려면 noSuchMethod()를 오버라이드하세요:
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); // handled
}이 기법은 프록시 객체, 테스트를 위한 모의(mock) 클래스 또는 유연한 API를 만드는 데 유용하지만, 컴파일 타임 타입 검사를 우회하므로 신중하게 사용해야 합니다.
직접 해보기
import 'method_logger.dart';
void main() {
// TODO: dynamic 타이핑을 사용하여 MethodLogger 인스턴스 생성하기
// dynamic을 사용하면 컴파일 타임에 존재하지 않는 메서드를 호출할 수 있습니다
dynamic logger;
// TODO: getUserData('Alice', 42)를 호출하고 결과를 출력하기
// TODO: saveRecord('important')를 호출하고 결과를 출력하기
// TODO: 인자 없이 processItem()을 호출하고 결과를 출력하기
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.