zero_cool zero_cool - 3 months ago 21
Groovy Question

Groovy make a class implement an interface via metaprogramming

Can I make a class implement an interface via Groovy's compile-time metaprogramming, and if so, how? I know that I can implement the methods defined in an interface for a given class. But how can I then cast an object of that class to that interface type?

Let's say I have an interface

public interface MyInterface {
void foo();
}


and a class

public class MyClass {

}


can I then provide a method
bar
with return type
MyInterface
that returns an instance of
MyClass
when called like

MyInterface mi = bar();
mi.foo();


and does not raise a
ClassCastException
?

Answer

Groovy features a couple of runtime ways to solve that, but I'm not aware of any "compile-time" solutions to it (beside implementing the interface, of course).

Anyway, you can coerce a class/map/closure to an interface easily. Here are some solutions:

1. as operator

I think this is the best for your case. A class is coerced into an interface. It is similar to Java proxies.

interface MyInterface {
  def foo()
}

class MyClass {
    def foo() { "foo" }
}

def bar() {
    return new MyClass() as MyInterface
}


MyInterface mi = bar()
assert mi.foo() == "foo"

2. Map coercion

A map can be coerced into an interface. You need to forward the method signatures, but it also gives more control over what is invoked and how it is invoked.

def mapped() {
    def m = new MyClass()
    [foo: { m.foo() }] as MyInterface
}

MyInterface mi2 = mapped()
assert mi2.foo() == "foo"

3. Anonymous class

Classical JDK < 8 style for a single method interface implementation.

def anonymous() {
    def m = new MyClass()
    new MyInterface() {
        def foo() {
            m.foo()
        }
    }
}

MyInterface mi3 = anonymous()
assert mi3.foo() == "foo"

4. Closure coercion

This one works a lot like JDK 8 lambda coercion. In this case, the method is returning a method reference to m.foo coerced into MyInterface. Be aware closure coercion is way more powerful than this, being able to coerce into abstract classes, concrete classes and splitting the atom:

def coercion() {
    def m = new MyClass()
    m.&foo as MyInterface
}

MyInterface mi4 = coercion()
assert mi4.foo() == "foo"