Fato: mais da metade das pessoas que conheço e programam em Groovy nunca usaram invocação dinâmica de métodos simplesmente por não saber o que é ou simplesmente como funciona. Sendo assim, sem mais delongas, vamos por a mão na massa:
Vamos supor que exista a classe PatoLouco implementada em Java tal como no código abaixo:
class PatoLouco { public void digaQua() { System.out.println("Qua!"); } public void digaQuaQua() { System.out.println("Qua Qua!"); } }
Em Java, se quisermos invocar um dos métodos presentes na classe, temos de satisfazer um requisito básico: ele deve estar implementado, pois isto é verificado em tempo de compilação pela linguaem. De acordo com este ponto de vista, temos duas alternativas: ou a própria classe já implementa estes métodos OU a classe implementa uma interface como a descrita abaixo:
public interface Pato { public void digaQua(); public void digaQuaQua(); } /* E seguindo esta lógica, poderemos ter zilhões de tipos diferentes de patos, tal como a classe abaixo. */ public class PatoNaoTaoLouco implements Pato { public void digaQua() { System.out.println("um qua não tão louco assim"); } public void digaQuaQua() { System.out.println("Qual a razão de ter de dizer dois 'quas'?"); } }
A presença das interfaces nos garante que os métodos serão implementados pelas nossas classes do tipo Pato. Se em um futuro não tão distante quanto parece formos preguiçosos em nossa modelagem e quisermos lidar com galinhas e outros tipos de aves, iriamos criando nossas interfaces, até chegar a um ponto no qual teriamos uma classe tal como a abaixo:
public class AveOrnitorrinca implements Pato, Galinha, Avestruz, Ganso, Aguia, Marreco { // zilhões de métodos implementados de acordo com as interfaces }
Linguagens dinâmicas como Groovy resolvem este problema aplicando o “princípio do pato”: se anda como um pato, corre como um pato e ‘fala’ como um pato, é porque é um pato. Sendo assim, vou apresentar um dos exemplos mais batidos deste principio no código Groovy abaixo:
class Cachorro { def incomode() {println "Lato sem parar. Au au auuuuu!"} } class Gato { def incomode() {println "Te arranho sem parar. Grite!"} } // repare que não há aplicação de herança ou interfaces. // o código abaixo executará normalmente instancias = [new Gato(), new Cachorro()] for (instancia in instancias) { instancia.incomode() }
A saída que teremos será:
Lato sem parar. Au au auuuuu! Te arranho sem parar. Grite!
Uma solução muito mais limpa do que em Java não é mesmo? Isto porque a verificação dos métodos é feita em tempo de execução. Na realidade, é possível ir além. Observe o código abaixo:
class CachorroLouco { def lata() {println "Au!"} def deite() {println "Deitado"} def role() {println "Rolando!"} def digaIsto(isto) {println isto} } /* Criei uma matriz de strings contendo os nomes dos métodos acima*/ nomeDosMetodos = ["lata", "deite", "role"] /* E agora, um pouco de "mágica" */ cao = new CachorroLouco() for (metodo in nomeDosMetodos) { cao."${metodo}"() } // Claro, o código abaixo também é válido cao."lata"() // E com parâmetros, seria a mesma coisa: cao."digaIsto"("Isto!")
Não é legal? Passando uma string para a minha instância, em tempo de execução eu posso invocar métodos dinâmicamente, o que abre uma gama imensa de possibilidades.
O que acontece se o método não existir?
new CachorroLouco()."faca_algo_impensado"() // Groovy me dará esta saida: No signature of method: CachorroLouco.faca_algo_impensado() is applicable for argument types (...)
Uma excessão do tipo groovy.lang.MissingMethodException será disparada. No entanto, podemos resolver este problema sobrescrevendo o métodomethodMissing em nossa classe, tal como no exemplo abaixo:
public class CachorroLouco { def methodMissing(String name, args) { println "O método ${name} não foi implementado seu perdido!" } // restante da classe abaixo }
Quando um método não é encontrado em uma classe, Groovy irá executar este método que, se for sobrescrito, poderá lidar com o seu caso específico. E neste momento você me pergunta: e se for uma classe implementada em outra linguagem, como Java, por exemplo: como lidar com esta situação?
Ai entra a nosso amigo (ou seria amiga?) ExpandoMetaClass
Groovy nos permite incluir novos comportamentos em classes já existentes. Fazemos isto usando a meta classe ExpandoMetaClass, que nos permite adicionar novos métodos, construtores e propriedades usando a sintaxe da closure que já conhecemos.
Sendo assim, o código abaixo é perfeitamente válido:
String.<strong>metaClass</strong>.sempreImprimaIsto = { -> println "Sempre imprimirei isto" } "sou uma nova string".sempreImprimaIsto() //método incluido na hora. // Lembra dos codecs de string do Grails? Funcionam exatamente assim.
Em nosso caso, bastaria injetar o método methodMissing em nossa classe CachorroLouco, exatamente como no exemplo abaixo:
CachorroLouco.metaClass.methodMissing = {String methodName, args -> println "O método não existe" }
É ou não é MUITO legal?
Deixe uma resposta