Есть две вещи, которые стоит объяснить, чтобы понять, что здесь происходит. В предоставленном вами сценарии есть две разные области действия.
Переменная 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 }