如何使用Java中的“求值”方法来使用Groovy脚本中定义的函数

时间:2018-10-29 14:19:49

标签: java groovy

我只是从Java中发现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]

要使用valuate()方法访问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脚本:

常规文件:

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") );

这正是我想要的:-)

2 个答案:

答案 0 :(得分:2)

有两件事值得解释,以了解此处发生的情况。您提供的脚本中有两个不同的作用域。

变量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"));

在此示例中,我们仅通过a来获取gs.evaluate("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 }

答案 1 :(得分:0)

让您拥有/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)" ) );