Переопределение noSuchMethod
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 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(); // Вызвано: Symbol("anyMethod")
obj.anotherOne(42); // Вызвано: Symbol("anotherOne")
}Объект Invocation предоставляет полезную информацию о вызове — memberName возвращает имя метода в виде Symbol, positionalArguments содержит переданные аргументы, а isMethod сообщает, был ли это вызов метода, а не геттера или сеттера.
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
}Эта техника полезна для создания прокси-объектов, мок-классов для тестирования или создания гибких API, которые реагируют на произвольные вызовы методов. Однако используйте её экономно, так как она обходит проверку типов во время компиляции.
Задание
ЛегкоДавайте создадим логгер методов, который перехватывает и записывает любые вызовы методов объекта! Вы создадите класс, использующий noSuchMethod() для захвата информации о вызовах неопределенных методов и интеллектуального ответа на основе имени метода.
Вы организуете свой код в два файла:
method_logger.dart: Создайте классMethodLogger, который перехватывает вызовы неопределенных методов. Ваш логгер должен переопределятьnoSuchMethod(), чтобы:- Извлечь имя метода из вызова (это будет Symbol, например
Symbol("methodName")— вам нужно будет преобразовать его в строку и извлечь только имя) - Вывести имя метода и любые позиционные аргументы в следующем формате:
Called: [methodName] with args: [arguments] - Вернуть специальный ответ в зависимости от имени метода:
- Если имя метода содержит
get, вернуть строку'data retrieved' - Если имя метода содержит
save, вернуть строку'data saved' - В противном случае вернуть строку
'unknown action'
- Если имя метода содержит
- Извлечь имя метода из вызова (это будет Symbol, например
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() без аргументов и выведите результат
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий