2009-11-19 20 views
5

Groovy ofrece algunas características de lenguaje realmente útiles para tratar e implementar interfaces Java, pero parece que estoy atascado.Implemente la interfaz de forma dinámica en Groovy usando invokeMethod

Quiero implementar dinámicamente una interfaz en una clase Groovy e interceptar todas las llamadas a métodos en esa interfaz usando GroovyInterceptable.invokeMethod. Aquí lo que he intentado hasta ahora:

public interface TestInterface 
{ 
    public void doBla(); 
    public String hello(String world); 
} 


import groovy.lang.GroovyInterceptable; 

class GormInterfaceDispatcher implements GroovyInterceptable 
{ 
    def invokeMethod(String name, args) { 
     System.out.println ("Beginning $name with $args") 
     def metaMethod = metaClass.getMetaMethod(name, args) 
     def result = null 
     if(!metaMethod) 
     { 
      // Do something cool here with the method call 

     } 
     else 
      result = metaMethod.invoke(this, args) 
     System.out.println ("Completed $name") 
     return result 
    } 

    TestInterface getFromClosure() 
    { 
     // This works, but how do I get the method name from here? 
     // I find that even more elegant than using invokeMethod 
     return { Object[] args -> System.out.println "An unknown method called with $args" }.asType(TestInterface.class) 
    } 


    TestInterface getThisAsInterface() 
    { 
     // I'm using asType because I won't know the interfaces 
     // This returns null 
     return this.asType(TestInterface.class) 
    } 

    public static void main(String[] args) 
    { 
     def gid = new GormInterfaceDispatcher() 
     TestInterface ti = gid.getFromClosure() 
     assert ti != null 
     ti.doBla() // Works 
     TestInterface ti2 = gid.getThisAsInterface() 
     assert ti2 != null // Assertion failed 
     ti2.doBla() 
    } 
} 

Volviendo al Cierre funciona bien, pero no podía imaginar una manera de averiguar el nombre del método que se llama allí.

Intentando hacer un Proxy a la referencia misma (para que las llamadas al método invoquen invokeMethod) devuelve nulo.

Respuesta

9

Se puede utilizar la función de Mapa coacción maravilloso para generar dinámicamente un mapa que representa la interfaz dada:

TestInterface getMapAsInterface() { 
    def map = [:] 

    TestInterface.class.methods.each() { method -> 
    map."$method.name" = { Object[] args-> 
     println "Called method ${method.name} with ${args}" 
    } 
    }  

    return map.asType(TestInterface.class) 
} 
+0

Muchas gracias, funciona como un amuleto y se ve limpio y todavía puedo acceder a todas las variables y métodos de los miembros. – Daff

+0

Tal como está, esto no funciona para interfaces que tienen métodos sobrecargados. –

0

Para completar la respuesta de Christoph, según lo declarado por este page, se puede implementar una interfaz con un cierre. Por ejemplo:

def map = [doBla: { println 'Bla!'}, hello: {world -> "Hello $world".toString()}] as TestInterface 
map.hello 'Groovy' // returns 'Hello Groovy' 
Cuestiones relacionadas