ClosureParams:如何在groovy中为闭包参数指定泛型类型

cyb*_*oft 5 java groovy closures

我在Java中有以下方法:

public void myMethod(
        @ClosureParams(
                value = SimpleType.class,
                options = {
                        "java.util.Map"
                }
        ) Closure<String> closure
) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

它必须@ClosureParams为 IDEA 中的静态类型检查器和类型推断指定闭包的参数类型。

在 Groovy 脚本中,我按如下方式调用此方法:

myMethod { Map<String, Object> doc ->
    ...
}
Run Code Online (Sandbox Code Playgroud)

而且效果很好。但是当我尝试在 java 方法中指定闭包的泛型类型时:java.util.Map

public void myMethod(
        @ClosureParams(
                value = SimpleType.class,
                options = {
                        "java.util.Map<java.lang.String,java.lang.Object>" // <-- added here
                }
        ) Closure<String> closure
) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

groovy 的静态类型检查器失败并出现错误:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
C:\myproject\script.groovy: 1: Expected parameter of type java.util.Map<java.lang.String,java.lang.Object> but got java.util.Map <String, Object>
 @ line 1, column 8.
myMethod { Map<String, Object> doc ->
Run Code Online (Sandbox Code Playgroud)

doc尽管IDEA在没有任何MapMap<...>使用提示的情况下推断出类型@ClosureParams

当我查看groovy.transform.stc.SimpleType类的源代码时,我发现该类无法指定泛型类型,因为它使用 plain Class.forName

public class SimpleType extends SingleSignatureClosureHint {
    @Override
    public ClassNode[] getParameterTypes(final MethodNode node, final String[] options, final SourceUnit sourceUnit, final CompilationUnit unit, final ASTNode usage) {
        ClassNode[] result = new ClassNode[options.length];
        for (int i = 0; i < result.length; i++) {
            result[i] = findClassNode(sourceUnit, unit, options[i]);
        }
        return result;
    }
 }

    // findClassNode method:
    protected ClassNode findClassNode(final SourceUnit sourceUnit, final CompilationUnit compilationUnit, final String className) {
        if (className.endsWith("[]")) {
            return findClassNode(sourceUnit, compilationUnit, className.substring(0, className.length() - 2)).makeArray();
        }
        ClassNode cn = compilationUnit.getClassNode(className);
        if (cn == null) {
            try {
                cn = ClassHelper.make(Class.forName(className, false, sourceUnit.getClassLoader()));
            } catch (ClassNotFoundException e) {
                cn = ClassHelper.make(className);
            }
        }
        return cn;
    }
Run Code Online (Sandbox Code Playgroud)

我的问题:如何在 groovy 中使用泛型指定闭包参数类型?最好有 IDEA 的支持。

Szy*_*iak 6

您可以使用groovy.transform.stc.FromString签名提示来使泛型类型正常工作。考虑以下示例:

Java类.java

import groovy.lang.Closure;
import groovy.transform.stc.ClosureParams;
import groovy.transform.stc.FromString;

import java.util.HashMap;

public class JavaClass {

    public static void processRendered(@ClosureParams(
            value = FromString.class,
            options = {"java.util.Map<java.lang.String,java.lang.Object>"}) Closure closure) {

        closure.call(new HashMap<String, Object>());
    }
}
Run Code Online (Sandbox Code Playgroud)

脚本.groovy

import groovy.transform.CompileStatic
import static JavaClass.processRendered

@CompileStatic
def test() {
  processRendered { Map<String, Object> map ->
    map.put("test", 1)
  }

  processRendered {
    it.put("test", 2)
  }
}

test()
Run Code Online (Sandbox Code Playgroud)

它编译并为您提供签名提示,也适用于隐式it变量。

在此输入图像描述

以下示例使用 Groovy 2.5.7

  • 是的,你是对的,现在可以了。Javadoc 提到了这一点:“其中每个字符串对应一个签名”,但我认为它的工作方式与“SimpleType”类似。一般来说,groovy doc 对这些事情没有那么详细。非常感谢!抱歉我的英语不好。 (2认同)