Nadpisanie noSuchMethod
Część sekcji Programowanie obiektowe ścieżki Dart w Coddy. Lekcja 74 z 110.
Gdy wywołasz metodę, która nie istnieje w obiekcie, Dart zwykle zgłasza NoSuchMethodError. Możesz jednak nadpisać specjalną metodę noSuchMethod(), aby przechwytywać te wywołania i obsługiwać je w odpowiedni sposób.
Aby używać noSuchMethod(), Twoja klasa musi również implementować interfejs lub korzystać z typowania dynamic.
Metoda otrzymuje obiekt Invocation zawierający szczegóły dotyczące próby wywołania:
class Flexible {
@override
dynamic noSuchMethod(Invocation invocation) {
var methodName = invocation.memberName.toString();
print('Called: $methodName');
return null;
}
}
void main() {
dynamic obj = Flexible();
obj.anyMethod(); // Wywołano: Symbol("anyMethod")
obj.anotherOne(42); // Wywołano: Symbol("anotherOne")
}Obiekt Invocation dostarcza przydatnych informacji o wywołaniu — memberName podaje nazwę metody jako symbol, positionalArguments zawiera przekazane argumenty, a isMethod informuje, czy było to wywołanie metody, czy getter lub 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
}Ta technika jest przydatna przy tworzeniu obiektów proxy, klas atrap do testowania lub elastycznych interfejsów API, które reagują na dowolne wywołania metod. Używaj jej jednak oszczędnie, ponieważ omija kontrolę typów w czasie kompilacji.
Wyzwanie
ŁatwyZbudujmy rejestrator metod, który przechwytuje i zapisuje wszystkie wywołania metod wykonywane na obiekcie! Utworzysz klasę, która używa noSuchMethod() do przechwytywania informacji o wywołaniach niezdefiniowanych metod i inteligentnego reagowania na podstawie nazwy metody.
Podzielisz kod na dwa pliki:
method_logger.dart: Utwórz klasęMethodLogger, która przechwytuje wywołania niezdefiniowanych metod. Twój rejestrator powinien nadpisywaćnoSuchMethod(), aby:- Wyodrębnić nazwę metody z wywołania (będzie to Symbol w rodzaju
Symbol("methodName")— trzeba przekonwertować go na ciąg znaków i wyodrębnić samą nazwę) - Wypisać nazwę metody i wszystkie argumenty pozycyjne w tym formacie:
Called: [methodName] with args: [arguments] - Zwrócić specjalną odpowiedź na podstawie nazwy metody:
- Jeśli nazwa metody zawiera
get, zwrócić ciąg znaków'data retrieved' - Jeśli nazwa metody zawiera
save, zwrócić ciąg znaków'data saved' - W przeciwnym razie zwrócić ciąg znaków
'unknown action'
- Jeśli nazwa metody zawiera
- Wyodrębnić nazwę metody z wywołania (będzie to Symbol w rodzaju
main.dart: Zaimportuj rejestrator i pokaż, jak obsługuje różne wywołania niezdefiniowanych metod. Pamiętaj, aby użyć typowaniadynamic, aby Dart pozwolił na wywoływanie metod, które nie istnieją:- Utwórz instancję
MethodLoggeri przypisz ją do zmiennej typu dynamicznego - Wywołaj na niej
getUserData('Alice', 42)i wypisz zwrócony wynik - Wywołaj na niej
saveRecord('important')i wypisz zwrócony wynik - Wywołaj na niej
processItem()(bez argumentów) i wypisz zwrócony wynik
- Utwórz instancję
Aby wyodrębnić nazwę metody z symbolu Symbol, możesz przekonwertować go na ciąg znaków, a następnie wyodrębnić nazwę znajdującą się między cudzysłowami. Na przykład Symbol("myMethod").toString() zwraca 'Symbol("myMethod")'.
Oczekiwany wynik:
Called: getUserData with args: [Alice, 42]
data retrieved
Called: saveRecord with args: [important]
data saved
Called: processItem with args: []
unknown actionSpróbuj swoich sił
import 'method_logger.dart';
void main() {
// TODO: Utwórz instancję MethodLogger, używając typowania dynamicznego
// Użycie dynamic pozwala wywoływać metody, które nie istnieją w czasie kompilacji
dynamic logger;
// TODO: Wywołaj getUserData('Alice', 42) i wypisz wynik
// TODO: Wywołaj saveRecord('important') i wypisz wynik
// TODO: Wywołaj processItem() bez argumentów i wypisz wynik
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP
Pliki zewnętrzneBiblioteki i importyWprowadzenie do OOPKlasy a obiektySłowo kluczowe thisMetodyZmienne instancjiPodstawy konstruktorówPowtórka – prosty kalkulator4Bezpieczeństwo wartości null
Wprowadzenie do bezpieczeństwa wartości nullTypy dopuszczające null i niedopuszczające nullOperatory ? i !Słowo kluczowe late a bezpieczeństwo wartości nullOperatory uwzględniające nullBezpieczeństwo wartości null w klasachPodsumowanie — system profili użytkowników7Klasy abstrakcyjne i interfejsy
Klasy abstrakcyjneMetody abstrakcyjneInterfejsy w DartInterfejsy niejawneImplementowanie a rozszerzanieWiele interfejsówPowtórka — kalkulator kształtów10Kolekcje i typy generyczne
Przegląd List, Set i MapKolekcje bezpieczne typowoKlasy generyczneMetody generyczneOgraniczenia typów generycznychIterable i IteratorPowtórka — generyczne przechowywanie danych13Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieMetody rozszerzająceKlasy wywoływalneKlasy zapieczętowane (Dart 3)Rekordy (Dart 3)Wzorce i dopasowywanie (3.0)Typy wyliczeniowe z metodami16Projekt: Zarządzanie biblioteką
Omówienie projektuKlasy książek i użytkowników2Konstruktory w Dart
Konstruktor domyślnyKonstruktory nazwaneListy inicjalizująceKonstruktory stałeKonstruktory fabryczneKonstruktory przekierowującePodsumowanie — budowanie kształtów5Hermetyzacja
Elementy publiczne a prywatneKonwencja prefiksu _Prywatność na poziomie bibliotekiGettery i settery — szczegółowoUkrywanie informacjiPodsumowanie — dane uczniów8Domieszki
Wprowadzenie do domieszekTworzenie domieszekKorzystanie z wielu domieszekSłowo kluczowe on w domieszkachDomieszka a dziedziczenieDomieszka a interfejsPodsumowanie – system zwierząt11Metody specjalne
Nadpisanie toString()Nadpisanie hashCode i ==Interfejs ComparableMetoda call()Nadpisanie noSuchMethodPodsumowanie — własna kolekcja14Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy3Właściwości klas
Elementy instancji a elementy statycznePola final i constZmienne inicjalizowane z opóźnieniemMetody i pola statyczneGettery i setteryPowtórzenie – menedżer kont bankowych6Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe superPrzesłanianie metodAdnotacja @overrideSłowo kluczowe final dla klasKonstruktory i dziedziczeniePowtórzenie – hierarchia pracowników9Polimorfizm
Podstawy polimorfizmuPolimorfizm za pomocą interfejsówSprawdzanie typu w czasie działaniaOperatory is i asSłowo kluczowe covariantPodsumowanie — procesor płatnościPoćwicz samodzielnie: Kompilator Dart online