Menu
Coddy logo textTech

Sobrescrita de noSuchMethod

Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 74 de 110.

Quando você chama um método que não existe em um objeto, o Dart normalmente lança um NoSuchMethodError. No entanto, você pode sobrescrever o método especial noSuchMethod() para interceptar essas chamadas e tratá-las de forma adequada.

Para usar noSuchMethod(), sua classe também deve implementar uma interface ou usar tipagem dynamic.

O método recebe um objeto Invocation contendo detalhes sobre a chamada tentada:

class Flexible {
  @override
  dynamic noSuchMethod(Invocation invocation) {
    var methodName = invocation.memberName.toString();
    print('Called: $methodName');
    return null;
  }
}

void main() {
  dynamic obj = Flexible();
  obj.anyMethod();      // Chamado: Symbol("anyMethod")
  obj.anotherOne(42);   // Chamado: Symbol("anotherOne")
}

O objeto Invocation fornece informações úteis sobre a chamada - memberName fornece o nome do método como um Symbol, positionalArguments contém os argumentos passados, e isMethod informa se foi uma chamada de método em vez de um getter ou 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
}

Esta técnica é útil para criar objetos proxy, classes mock para testes ou construir APIs flexíveis que respondem a chamadas de métodos arbitrárias. No entanto, use-a com moderação, já que ela ignora a verificação de tipos em tempo de compilação.

challenge icon

Desafio

Fácil

Vamos construir um registrador de métodos (method logger) que intercepta e registra quaisquer chamadas de método feitas em um objeto! Você criará uma classe que usa noSuchMethod() para capturar informações sobre chamadas de métodos indefinidos e responder de forma inteligente com base no nome do método.

Você organizará seu código em dois arquivos:

  • method_logger.dart: Crie uma classe MethodLogger que intercepta chamadas para métodos indefinidos. Seu registrador deve sobrescrever noSuchMethod() para:
    • Extrair o nome do método da invocação (será um Symbol como Symbol("methodName") - você precisará convertê-lo em uma string e extrair apenas o nome)
    • Imprimir o nome do método e quaisquer argumentos posicionais neste formato: Called: [methodName] with args: [arguments]
    • Retornar uma resposta especial baseada no nome do método:
      • Se o nome do método contiver get, retorne a string 'data retrieved'
      • Se o nome do método contiver save, retorne a string 'data saved'
      • Caso contrário, retorne a string 'unknown action'
  • main.dart: Importe seu registrador e demonstre como ele lida com várias chamadas de métodos indefinidos. Lembre-se de usar tipagem dynamic para que o Dart permita chamar métodos que não existem:
    • Crie uma instância de MethodLogger usando uma variável dinâmica
    • Chame getUserData('Alice', 42) nela e imprima o resultado retornado
    • Chame saveRecord('important') nela e imprima o resultado retornado
    • Chame processItem() nela (sem argumentos) e imprima o resultado retornado

Para extrair o nome do método de um Symbol, você pode convertê-lo em uma string e então analisar o nome entre as aspas. Por exemplo, Symbol("myMethod").toString() resulta em 'Symbol("myMethod")'.

Saída esperada:

Called: getUserData with args: [Alice, 42]
data retrieved
Called: saveRecord with args: [important]
data saved
Called: processItem with args: []
unknown action

Folha de consulta

O método noSuchMethod() permite interceptar chamadas para métodos que não existem em um objeto. Para usá-lo, sua classe deve usar tipagem dynamic ou implementar uma interface.

Sobrescreva noSuchMethod() para lidar com chamadas de métodos indefinidos:

class Flexible {
  @override
  dynamic noSuchMethod(Invocation invocation) {
    var methodName = invocation.memberName.toString();
    print('Called: $methodName');
    return null;
  }
}

void main() {
  dynamic obj = Flexible();
  obj.anyMethod();      // Chamado: Symbol("anyMethod")
  obj.anotherOne(42);   // Chamado: Symbol("anotherOne")
}

O objeto Invocation fornece informações sobre a chamada:

  • memberName - o nome do método como um Symbol
  • positionalArguments - os argumentos passados para o método
  • isMethod - indica se foi uma chamada de método em vez de um getter ou 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
}

Esta técnica é útil para criar objetos proxy, classes mock para testes ou APIs flexíveis, mas deve ser usada com moderação, pois ignora a verificação de tipos em tempo de compilação.

Experimente você mesmo

import 'method_logger.dart';

void main() {
  // TODO: Criar uma instância de MethodLogger usando tipagem dinâmica
  // O uso de dynamic permite chamar métodos que não existem em tempo de compilação
  dynamic logger;
  
  // TODO: Chamar getUserData('Alice', 42) e imprimir o resultado
  
  // TODO: Chamar saveRecord('important') e imprimir o resultado
  
  // TODO: Chamar processItem() sem argumentos e imprimir o resultado
  
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos