Как использовать функцию, определенную в Groovy скрипте, используя метод «оценки» из Java - PullRequest
0 голосов
/ 29 октября 2018

Я только что обнаружил вызов Groovy из Java и у меня возникла проблема с этим случаем:

У меня есть отличный файл: "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]

Что мне нужно сделать, чтобы получить доступ к функции, определенной в groovy-скрипте, с использованием методаvalu ()?

Мне нужно использовать метод «оценки», потому что я хочу, наконец, оценить что-то вроде Math.sin( a * mul2(Math.Pi) ).


Теперь у меня есть 4 решения (четвертое - это то, что я искал):

  1. используйте закрытие как в ответе 'Szymon Stepniak'
  2. использовать статический импорт как в ответе '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. И, наконец, расширяет заводной скрипт:

заводной файл:

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 голосов
/ 29 октября 2018

пусть у вас будет /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)" ) );
0 голосов
/ 29 октября 2018

Есть две вещи, которые стоит объяснить, чтобы понять, что здесь происходит. В предоставленном вами сценарии есть две разные области действия.

Переменная 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 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...