Groovy使一个类通过元编程实现接口

Eri*_*fer 2 groovy

我可以使类通过Groovy的编译时元编程实现接口吗?如果可以,如何实现?我知道我可以实现给定类的接口中定义的方法。但是,如何将此类的对象转换为该接口类型呢?

假设我有一个界面

public interface MyInterface {
  void foo();
}
Run Code Online (Sandbox Code Playgroud)

和一堂课

public class MyClass {

}
Run Code Online (Sandbox Code Playgroud)

可以予然后提供一种方法,bar与返回类型MyInterface返回的实例MyClass调用时等

MyInterface mi = bar();
mi.foo();
Run Code Online (Sandbox Code Playgroud)

并且不提出ClassCastException

Wil*_*ill 5

Groovy提供了几种运行时方法来解决该问题。当然,对于编译时,@Delegate除了实现接口外,还可能要看一些东西。

无论如何,您可以轻松地将类/映射/闭包强制转换为接口。以下是一些解决方案:

1. as操作员

我认为这是最适合您的情况。一个类被强制进入一个接口。它类似于Java代理

interface MyInterface {
  def foo()
}

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

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


MyInterface mi = bar()
assert mi.foo() == "foo"
Run Code Online (Sandbox Code Playgroud)

2.地图强制

可以将地图强制为接口。您需要转发方法签名,但它还可以更好地控制调用什么以及如何调用。

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

MyInterface mi2 = mapped()
assert mi2.foo() == "foo"
Run Code Online (Sandbox Code Playgroud)

3.匿名班

单一方法接口实现的经典JDK <8样式。

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

MyInterface mi3 = anonymous()
assert mi3.foo() == "foo"
Run Code Online (Sandbox Code Playgroud)

4.强制关闭

这一功能非常类似于JDK 8 Lambda强制。在这种情况下,该方法将返回方法参考以m.foo强制转换为MyInterface。请注意,闭包强制比这要强大得多,它可以强制为抽象类,具体类并拆分原子

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

MyInterface mi4 = coercion()
assert mi4.foo() == "foo"
Run Code Online (Sandbox Code Playgroud)

5. @Delegate设有子类工厂

更新:您可以创建一个类,该类的@Delegates所有方法调用都可以对对象进行响应,而无需实现它们就可以响应所有接口。

请注意,如果MyClass未实现所有必需的方法,则会引发编译错误。

对于子类,可以使用工厂方法:

interface Foo { def foo() }
interface Bar { def bar() }
interface Baz { def baz() }

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

class MySubClass extends MyClass {
    def foo() { "sub foo" }
}

class MyDelegate implements Foo, Bar, Baz {
    @Delegate MyClass my

    static getSub() {
        new MyDelegate(my : new MySubClass())
    }
}

MyDelegate.sub.with {
    assert foo() == "sub foo"
    assert bar() == "bar"
    assert baz() == "baz"
}
Run Code Online (Sandbox Code Playgroud)