【问题标题】:How to use function defined in a groovy script using the "evaluate" method from Java如何使用 Java 中的“评估”方法使用 groovy 脚本中定义的函数
【发布时间】:2026-01-29 00:05:01
【问题描述】:

我刚刚发现来自 Java 的 Groovy 调用,并且对这种情况有疑问:

我有一个 groovy 文件:“test.groovy

a = 1.0
def mul2( x ) { 2.0 * x }

我想像这样从 Java 代码中使用它

GroovyShell gs = new GroovyShell();
gs.parse( new File( ".../test.groovy" ) ).run();

System.out.printf( "a = %s%n", gs.evaluate("a") ); // ok
System.out.printf( "mul2(a) = %s%n", gs.evaluate( "mul2(a)" ) ); // error

错误是:

groovy.lang.MissingMethodException: No signature of method: Script1.mul2() is applicable for argument types: (BigDecimal) values: [1.0]

使用 evaluate() 方法访问 groovy 脚本中定义的函数需要做什么?

我需要使用“评估”方法,因为我想最终评估像Math.sin( a * mul2(Math.Pi) ) 这样的东西。


现在我有 4 个解决方案(第四个是我搜索的):

  1. 在回答“Szymon Stepniak”时使用闭包
  2. 使用 import static 作为 'daggett' 的答案
  3. 使用评估表达式的脚本扩展包含 Java 函数的脚本:

...类(在 Java 中,不是 Groovy)...

  public static abstract class ScriptClass extends Script
  {
    double mul2( double x )
    {
      return x * 2;
    }
  }

...代码...

  CompilerConfiguration config = new CompilerConfiguration();
  config.setScriptBaseClass(ScriptClass.class.getName());

  GroovyShell gs = new GroovyShell(config);

  System.out.printf( "result = %s%n", gs.evaluate("mul2(5.05)") );

这行得通,但代码是用 Java 编写的,不是我想要的,但我在这里记下了,因为需要这样做的人

  1. 最后扩展了 groovy 脚本:

groovy 文件:

double mul2( x ) { x * 2 } 
a=mul2(3.33)

使用它的java代码

GroovyClassLoader gcl = new GroovyClassLoader();
Class<?> r = gcl.parseClass( resourceToFile("/testx.groovy") );
CompilerConfiguration config = new CompilerConfiguration();
config.setScriptBaseClass(r.getName());
GroovyShell gs = new GroovyShell(gcl, config);    

System.out.printf( "mul2(5.05) = %s%n", gs.evaluate("mul2(5.05)") );

// WARNING : call super.run() in evaluate expression to have access to variables defined in script
System.out.printf( "result = %s%n", gs.evaluate("super.run(); mul2(a) / 123.0") );

这正是我想要的:-)

【问题讨论】:

  • 您在代码中混入了几个问题。最重要的是:使用gs.evaluate,您正在解析一个新的 groovy 脚本,并且绝对不会与以前解析的脚本相关联。

标签: java groovy


【解决方案1】:

有两件事值得解释以了解这里发生的事情。您提供的脚本中有两个不同的范围。

变量a 存储在GroovyShell 绑定对象中,这就是为什么它在每个gs.evaluate() 调用中都可用。看看这个例子:

import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;

final class ExecuteGroovyScriptExample {

    public static void main(String[] args) {
        final String script = "a = 1.0 \n" +
                "def mul2(x) { 2.0 * x }\n";

        final Binding binding = new Binding();

        final GroovyShell gs = new GroovyShell(binding);
        final Script sc = gs.parse(script);
        sc.run();

        System.out.printf("binding.getVariable(\"a\") == %s\n", binding.getVariable("a"));
    }
}

运行此示例会产生以下输出:

binding.getVariable("a") == 1.0

第二件事是每个gs.evaluate() 调用都会生成一个新的groovy.lang.Script 类,它具有完全不同的上下文。这就是为什么调用:

gs.evaluate("mul2(a)")

抛出类似这样的东西:

Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: Script2.mul2() is applicable for argument types: (BigDecimal) values: [1.0]

因为从gs.evaluate("mul2(a)") 调用生成的脚本类不包含mul2(x) 方法。此调用生成的类如下所示:

class Script2 extends groovy.lang.Script {
    void run() {
        mul2(a)
    }
}

但是,从gs.parse(script) 返回的脚本类包含mul2(x) 方法,因此您可以调用它,但不能像gs.evaluate() 那样调用,而是Script.invokeMethod(name, args)。像这样的:

import groovy.lang.GroovyShell;
import groovy.lang.Script;

final class ExecuteGroovyScriptExample {

    public static void main(String[] args) {
        final String script = "a = 1.0 \n" +
                "def mul2(x) { 2.0 * x }\n";

        final GroovyShell gs = new GroovyShell();
        final Script sc = gs.parse(script);
        sc.run();

        System.out.printf("mul2(a) = %s%n", sc.invokeMethod("mul2", gs.evaluate("a")));
    }
}

此示例产生以下输出:

mul2(a) = 2.00

看看mul2(x) 方法是如何被调用的。首先,我们将gs.parse(script)返回的脚本存储在sc变量中,它允许我们通过以下调用调用该脚本中定义的方法:

sc.invokeMethod("mul2", gs.evaluate("a"));

在本例中,我们仅通过gs.evaluate("a") 获取a 变量的值,但您也可以使用第一个示例中的binding 对象。请记住,如果 a 变量的定义如下:

def a = 1.0

@groovy.transform.Field
def a = 1.0

它将不再存储在binding 对象中,在第一种情况下,它定义脚本的局部变量a,在第二种情况下,它定义脚本类字段a


或者,如果您想执行以下调用:

gs.evaluate("mul2(a)")

甚至

gs.evaluate("Math.sin( a * mul2(Math.PI))")

您必须修改输入 Groovy 脚本文件并将函数 mul2(x) 定义替换为与 a 变量相同范围内的闭包,因此它被存储在 binding 对象中:

a = 1.0
mul2 = { x -> 2.0 * x }

【讨论】:

  • 非常感谢,但正如我在帖子最后一行解释的那样,我可以做到。我想在表达式中使用我的 groovy 文件中定义的函数。也许我必须扩展第一个脚本类并在其中评估我的表达式。但我不知道该怎么做......
  • 或者,您可以在 Groovy 脚本文件中将 mul2(x) 替换为像 mul2 = { x -&gt; 2.0 * x } 这样的闭包,然后将正确评估后续调用 gs.evaluate("Math.sin( a * mul2(Math.PI))")
  • 我已经编辑了最初的帖子,我想评估复杂的表达式,就好像这个表达式位于 groovy 文件的末尾一样。如果我使用第一个 groovy 文件来创建脚本(可能是抽象的)并将其扩展为评估复杂表达式的新脚本,这似乎是可能的。如果 Script 在 java 中,我已经成功,我努力在 Groovy 中拥有脚本......
【解决方案2】:

让你有/my/groovy/classes/Test.groovy:

static mul2( x ) { 2.0 * x }
def    mul3( x ) { 3.0 * x }

然后你可以使用类加载器将它作为一个类加载并在表达式中使用这个类:

GroovyShell gs = new GroovyShell();
gs.getClassLoader().addClasspath("/my/groovy/classes");

System.out.println( gs.evaluate( "import static Test.*; mul2(5)" ) );
System.out.println( gs.evaluate( "new Test().mul3(6)" ) );

【讨论】: