Sobrescritura de noSuchMethod
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 74 de 110.
Cuando llamas a un método que no existe en un objeto, Dart normalmente lanza un NoSuchMethodError. Sin embargo, puedes sobrescribir el método especial noSuchMethod() para interceptar estas llamadas y manejarlas de forma adecuada.
Para usar noSuchMethod(), su clase también debe implementar una interfaz o usar tipado dynamic.
El método recibe un objeto Invocation que contiene detalles sobre la llamada intentada:
class Flexible {
@override
dynamic noSuchMethod(Invocation invocation) {
var methodName = invocation.memberName.toString();
print('Called: $methodName');
return null;
}
}
void main() {
dynamic obj = Flexible();
obj.anyMethod(); // Llamado: Symbol("anyMethod")
obj.anotherOne(42); // Llamado: Symbol("anotherOne")
}El objeto Invocation proporciona información útil sobre la llamada - memberName le da el nombre del método como un Symbol, positionalArguments contiene los argumentos pasados, y isMethod le indica si fue una llamada a un método en lugar de un getter o 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); // manejado
}Esta técnica es útil para crear objetos proxy, clases mock para pruebas o para construir APIs flexibles que respondan a llamadas de métodos arbitrarios. Sin embargo, úsela con moderación ya que omite la comprobación de tipos en tiempo de compilación.
Desafío
Fácil¡Vamos a construir un registrador de métodos que intercepte y registre cualquier llamada a métodos realizada en un objeto! Crearás una clase que utilice noSuchMethod() para capturar información sobre llamadas a métodos no definidos y responder inteligentemente basándose en el nombre del método.
Organizarás tu código en dos archivos:
method_logger.dart: Crea una claseMethodLoggerque intercepte llamadas a métodos no definidos. Tu registrador debe sobrescribirnoSuchMethod()para:- Extraer el nombre del método de la invocación (será un Symbol como
Symbol("methodName"); deberás convertirlo a una cadena y extraer solo el nombre) - Imprimir el nombre del método y cualquier argumento posicional en este formato:
Called: [methodName] with args: [arguments] - Devolver una respuesta especial basada en el nombre del método:
- Si el nombre del método contiene
get, devuelve la cadena'data retrieved' - Si el nombre del método contiene
save, devuelve la cadena'data saved' - De lo contrario, devuelve la cadena
'unknown action'
- Si el nombre del método contiene
- Extraer el nombre del método de la invocación (será un Symbol como
main.dart: Importa tu registrador y demuestra cómo maneja varias llamadas a métodos no definidos. Recuerda usar tipadodynamicpara que Dart permita llamar a métodos que no existen:- Crea una instancia de
MethodLoggerusando una variable dinámica - Llama a
getUserData('Alice', 42)en ella e imprime el resultado devuelto - Llama a
saveRecord('important')en ella e imprime el resultado devuelto - Llama a
processItem()en ella (sin argumentos) e imprime el resultado devuelto
- Crea una instancia de
Para extraer el nombre del método de un Symbol, puedes convertirlo a una cadena y luego analizar el nombre entre las comillas. Por ejemplo, Symbol("myMethod").toString() te da 'Symbol("myMethod")'.
Salida esperada:
Called: getUserData with args: [Alice, 42]
data retrieved
Called: saveRecord with args: [important]
data saved
Called: processItem with args: []
unknown actionHoja de referencia
El método noSuchMethod() te permite interceptar llamadas a métodos que no existen en un objeto. Para usarlo, tu clase debe usar tipado dynamic o implementar una interfaz.
Sobrescribe noSuchMethod() para manejar llamadas a métodos no definidos:
class Flexible {
@override
dynamic noSuchMethod(Invocation invocation) {
var methodName = invocation.memberName.toString();
print('Called: $methodName');
return null;
}
}
void main() {
dynamic obj = Flexible();
obj.anyMethod(); // Llamado: Symbol("anyMethod")
obj.anotherOne(42); // Llamado: Symbol("anotherOne")
}El objeto Invocation proporciona información sobre la llamada:
memberName- el nombre del método como un SymbolpositionalArguments- los argumentos pasados al métodoisMethod- indica si fue una llamada a un método en lugar de un getter o 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); // manejado
}Esta técnica es útil para crear objetos proxy, clases mock para pruebas o APIs flexibles, pero debe usarse con moderación ya que omite la comprobación de tipos en tiempo de compilación.
Pruébalo tú mismo
import 'method_logger.dart';
void main() {
// TODO: Crear una instancia de MethodLogger usando tipado dinámico
// El uso de dynamic permite llamar a métodos que no existen en tiempo de compilación
dynamic logger;
// TODO: Llamar a getUserData('Alice', 42) e imprimir el resultado
// TODO: Llamar a saveRecord('important') e imprimir el resultado
// TODO: Llamar a processItem() sin argumentos e imprimir el resultado
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO
Archivos externosLibrerías e importacionesIntroducción a la POOClases vs. ObjetosLa palabra clave thisMétodosVariables de instanciaConceptos básicos de constructoresResumen - Calculadora sencilla4Seguridad de nulos
Introducción a la seguridad de nulosNulables vs No nulablesLos operadores ? y !Palabra clave Late y Null SafetyOperadores Null-awareNull Safety en clasesResumen - Sistema de perfil de usuario7Clases abstractas e interfaces
Clases abstractasMétodos abstractosInterfaces en DartInterfaces implícitasImplementar vs. ExtenderInterfaces múltiplesRepaso - Calculadora de figuras10Colecciones y Genéricos
Visión general de List, Set y MapColecciones con seguridad de tiposClases genéricasMétodos genéricosRestricciones genéricasIterable e IteratorResumen - Almacenamiento genérico2Constructores en Dart
Constructor por defectoConstructores nombradosListas de inicializaciónConstructores constantesConstructores de fábricaConstructores de redireccionamientoResumen - Constructor de formas5Encapsulamiento
Miembros públicos vs. privadosLa convención del prefijo _Privacidad a nivel de libreríaProfundidad en Getters y SettersOcultamiento de informaciónResumen - Registros de estudiantes8Mixins
Introducción a los MixinsCreación de MixinsUso de múltiples MixinsPalabra clave on en MixinsMixin vs HerenciaMixin vs InterfazResumen - Sistema Animal11Métodos especiales
Sobrescritura de toString()Sobrescritura de hashCode y ==Interfaz ComparableMétodo call()Sobrescritura de noSuchMethodResumen - Colección personalizada14Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy3Propiedades de clase
Miembros de instancia vs. estáticosCampos Final y ConstVariables LateMétodos y campos estáticosGetters y SettersResumen - Gestor de cuentas bancarias6Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosLa anotación @overrideLa palabra clave final en clasesConstructores y herenciaResumen - Jerarquía de empleados9Polimorfismo
Fundamentos del polimorfismoPolimorfismo mediante interfacesComprobación de tipos en tiempo de ejecuciónLos operadores is y asPalabra clave covariantResumen - Procesador de pagos12POO Asíncrona
Futures y async/awaitFundamentos de StreamsStream ControllersConstructores asíncronosAsincronía en métodos de claseRepaso - Data Fetcher15Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository