Menu
Coddy logo textTech

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.

challenge icon

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 clase MethodLogger que intercepte llamadas a métodos no definidos. Tu registrador debe sobrescribir noSuchMethod() 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'
  • main.dart: Importa tu registrador y demuestra cómo maneja varias llamadas a métodos no definidos. Recuerda usar tipado dynamic para que Dart permita llamar a métodos que no existen:
    • Crea una instancia de MethodLogger usando 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

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 action

Hoja 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 Symbol
  • positionalArguments - los argumentos pasados al método
  • isMethod - 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
  
}
quiz iconPonte a prueba

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