Groovy 如何使用trait中的@ClosureParams引用实现器类

Groovy 如何使用trait中的@ClosureParams引用实现器类,groovy,Groovy,我想将@ClosureParams与trait中的一个方法结合使用,该方法将闭包作为输入,调用时将传递给trait的实现者 考虑以下示例: trait Fooable { void foo(@ClosureParams(????) Closure callable) { callable.call(this) } } class Bar implements Fooable { String baz } new Bar().foo { it.baz =

我想将
@ClosureParams
与trait中的一个方法结合使用,该方法将闭包作为输入,调用时将传递给trait的实现者

考虑以下示例:

trait Fooable {
    void foo(@ClosureParams(????) Closure callable) {
        callable.call(this)
    }
}

class Bar implements Fooable {
    String baz
}

new Bar().foo { it.baz == "foo'ed" }

我如何告诉静态分析器传递给闭包的
it
实际上是
Bar
(最后一行)。在
foo
方法的定义中,我应该向@ClosureParams传递什么值?

它目前不适用于traits(Groovy2.4.13),因为没有实现允许您在运行时定义使用从trait接口实现方法的类类型的提示类型。如果您的trait仅由
Bar
类实现,则可以将闭包参数类型指定为:

@CompileStatic
@TypeChecked
trait Fooable {
    void foo(@ClosureParams(value = SimpleType, options = ["Bar"]) Closure callable) {
        callable.call(this)
    }
}
但事实并非如此

@ClosureParams
如果与trait一起使用,甚至无法识别泛型类型。让我们考虑下面的定义:

@CompileStatic
@TypeChecked
trait Fooable<T> {
    void foo(@ClosureParams(value = SimpleType, options = ["T"]) Closure callable) {
        callable.call(this)
    }
}
如果将
Bar.class
作为反编译文件打开,您将看到这一点

如果我们使用抽象类而不是trait,泛型将很好地工作。在这种情况下,抽象泛型类
Fooable
将实现
foo
方法,因此
Bar
类将引用
Fooable
类的实现-一个知道
T
类型的类。在这种情况下,IDE将正确解析
T
,并建议使用
Bar

那么,在这种情况下使用trait有哪些选择呢?您可以尝试实现自己的
ClosureSignatureHint
类,但这并不容易。我做了一个小实验——我定义了
NewSimpleType
类,并从
SimpleType
类复制了1:1的源代码。然后我把它用作:

@CompileStatic
@TypeChecked
trait Fooable {
    void foo(@ClosureParams(value = NewSimpleType, options = ["Bar"]) Closure callable) {
        callable.call(this)
    }
}
如您所见,我只是用我的自定义
NewSimpleType
替换了Groovy的
SimpleType
。它不起作用。我的IDE(IntelliJ IDEA Ultimate 2017.3.3)未解析任何类型。我甚至将这个类移动到一个单独的Maven项目中,我构建了它,并作为依赖项添加了它,但效果并不理想

我假设应该可以实现一个考虑调用方类类型的提示类。有些实现从第一个、第二个或第三个参数获取闭包参数类型。至少在理论上,这听起来是可行的

最后一个需要最少努力的选项是显式地提供闭包参数类型,例如

Bar bar = new Bar()
bar.foo { Bar b -> b.baz }
它支持所有代码完成功能。缺点是您可以指定不同的类型,如:

Bar bar = new Bar()
bar.foo { String b -> b.toLowerCase() } 
IDE不会对此抱怨,但编译时会失败

自定义
StringParameterHint
用例 我为实验创建了一个静态闭包签名提示,它只接受
java.lang.String
作为参数:

public class StringParameterHint extends ClosureSignatureHint {
    @Override
    public List<ClassNode[]> getClosureSignatures(MethodNode node, SourceUnit sourceUnit, CompilationUnit compilationUnit, String[] options, ASTNode usage) {
        final List<ClassNode[]> list = new ArrayList<>();
        list.add(GenericsUtils.parseClassNodesFromString("java.lang.String", sourceUnit, compilationUnit, node, usage));
        return list;
    }
}
IDE不会将其标记为不正确的表达式,但编译失败,程序不会启动:

Error:(11, 19) Groovyc: Expected parameter of type java.lang.String but got tld.company.Bar
Error:(11, 28) Groovyc: [Static type checking] - No such property: baz for class: java.lang.String

所以看起来我们可以强制编译器感知闭包参数,但IDE并没有读取这些信息(我的例子是IntelliJ IDEA 2017.3.3)。我想这可能是IDE的问题。我甚至将这个
StringParameterHint
类移动到了
groovy.transform.stc
包中(我假设IDE可能会自动加载这个包中的所有提示),但它没有帮助。

回答得很好。我想知道另一种方法是否是使用全局AST转换来检测类上使用的
Fooable
特性,并使用正确的
@SourceParams
注释直接修改/添加
foo
方法。在这种情况下,IDE是否能够使用
@SourceParams
,或者它是否会被隐藏,因为它只有在编译后才可见?不,不仅是Bar实现了可食性,许多其他POGO也会实现。@EgilHansen我担心它可能也不起作用,但它绝对值得一试。我能够使用定制的
StringParameterHint
类,该类只接受
String
作为闭包参数-它在编译器中运行良好,但IDE完全不知道这个参数类型约束。我已经更新了我的答案,提供了关于这个用例的更多细节。
bar.foo { Bar b -> b.baz }
Error:(11, 19) Groovyc: Expected parameter of type java.lang.String but got tld.company.Bar
Error:(11, 28) Groovyc: [Static type checking] - No such property: baz for class: java.lang.String