views:

574

answers:

2

Groovy offers some really neat language features for dealing with and implementing Java interfaces, but I seem kind of stuck.

I want to dynamically implement an Interface on a Groovy class and intercept all method calls on that interface using GroovyInterceptable.invokeMethod. Here what I tried so far:

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()
    }
}

Returning the Closure works fine, but I couldn't figure a way to find out the name of the method being called there.

Trying to make a Proxy to the this reference itself (so that method calls will call invokeMethod) returns null.

+2  A: 

You could use the Map coercion feature of Groovy to dynamically generate a Map that represents the given interface:

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)
}
Christoph Metzendorf
Thanks a lot, works like a charm and looks clean and I can still access all the member variables and methods.
Daff
A: 

To complete the response of Christoph, as stated by this page, you can implement an interface with a closure. For example:

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