GroovyCastException при запуске кода Java, содержащего новую строку "\n", с обработчиком сценариев groovy (GroovyClassLoader)

В настоящее время я работаю над способом запуска кода Java в виде строки. Так вот как я это сделал.

import java.util.HashMap;
import java.util.Map;

import groovy.lang.GroovyClassLoader;

public class GroovyStackru {

    public static void main(String[] args) {
        GroovyClassLoader gcl = new GroovyClassLoader();
        String codeSnippet = "double calculatedAnswer = (Double)"
                + "contextMap.get(\"doubleValue\") * (Double)contextMap.get(\"doubleValue\");"
                + " calculatedAnswer = Math.sqrt(calculatedAnswer); "
                + "calculatedAnswer = calculatedAnswer * calculatedAnswer;"
                + "System.out.println(calculatedAnswer);"
                + " return calculatedAnswer;";
        StringBuilder sb = new StringBuilder();
        sb.append("public class ScriptImplementor implements ScriptEvaluator { public Object evaluate(Map contextMap) {");
        sb.append(codeSnippet);
        sb.append("} }");
        Class<?> clazz = gcl.parseClass(sb.toString());
        ScriptEvaluator scriptEvaluator = null;     
        double calculatedAnswer = 100.0;        
        try {
            Map contextMap = new HashMap();
            contextMap.put("doubleValue", (double)100.0);
            contextMap.put("threadId", "thread"+100);
            contextMap.put("hashCode", 100);
            scriptEvaluator = (ScriptEvaluator) clazz.newInstance();
            scriptEvaluator.evaluate(contextMap);;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

public interface ScriptEvaluator {
    public Object evaluate(Map contextMap);
}

Проблема состоит в том, что это терпит неудачу в следующем случае.

import java.util.HashMap;
import java.util.Map;

import groovy.lang.GroovyClassLoader;

public class GroovyStackru {

    public static void main(String[] args) {
        GroovyClassLoader gcl = new GroovyClassLoader();
        String codeSnippet = "double calculatedAnswer = (Double)"
                + "\n "
                + "contextMap.get(\"doubleValue\") * (Double)contextMap.get(\"doubleValue\");"
                + " calculatedAnswer = Math.sqrt(calculatedAnswer); "
                + "calculatedAnswer = calculatedAnswer * calculatedAnswer;"
                + "System.out.println(calculatedAnswer);"
                + " return calculatedAnswer;";
        StringBuilder sb = new StringBuilder();
        sb.append("public class ScriptImplementor implements ScriptEvaluator { public Object evaluate(Map contextMap) {");
        //sb.append(codeSnippet.replaceAll("\n", " "));
        sb.append(codeSnippet);
        sb.append("} }");
        Class<?> clazz = gcl.parseClass(sb.toString());
        ScriptEvaluator scriptEvaluator = null;     
        double calculatedAnswer = 100.0;
        try {
            Map contextMap = new HashMap();
            contextMap.put("doubleValue", (double)100.0);
            contextMap.put("threadId", "thread"+100);
            contextMap.put("hashCode", 100);
            scriptEvaluator = (ScriptEvaluator) clazz.newInstance();
            scriptEvaluator.evaluate(contextMap);;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
public interface ScriptEvaluator {
    public Object evaluate(Map contextMap);
}

Я не понимаю, почему это не удается и что означает это сообщение об ошибке

Exception in thread "main" org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'class java.lang.Double' with class 'java.lang.Class' to class 'double'
    at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToNumber(DefaultTypeTransformation.java:163)
    at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.doubleUnbox(DefaultTypeTransformation.java:88)
    at ScriptImplementor.evaluate(script15126616543572010791987.groovy:1)
    at GroovyStackru.main(GroovyStackru.java:33)

После раскомментирования этого кода //sb.append(codeSnippet.replaceAll("\n", " ")); оно работает. Но, пожалуйста, предложите лучший способ справиться с этим. И почему он не выдает ошибку при разборе класса? И какие еще сюрпризы я могу ожидать, как это?

1 ответ

Решение

Вы столкнулись с разницей между Java и Groovy.

В Java оператор заканчивается точкой с запятой.

В Groovy satement заканчивается точкой с запятой или переводом строки, если оператор уже является полным оператором.

В вашем случае это означает, что код

double calculatedAnswer = (Double)
contextMap.get("doubleValue") * (Double)contextMap.get("doubleValue")

это два заявления.

Первое из этих утверждений double calculatedAnswer = (Double),

В Groovy вы также можете опустить .class ссылаться на класс, так Double.class можно записать как Double,

Так что вы делаете в этом утверждении, что вы назначаете Double объект класса к double переменная. Скобки здесь просто неактивны.

Это, конечно, не так, как говорится в сообщении, так как Double объект класса не может быть автоматически double,

Вы можете явно избежать переноса строки, чтобы она не заканчивала оператор, как в

double calculatedAnswer = (Double)\
contextMap.get("doubleValue") * (Double)contextMap.get("doubleValue")

который будет работать, как вы ожидали.

Но, конечно, могут быть и другие случаи, когда Groovy и Java отличаются.

Всегда помните, что синтаксис Groovy близок к синтаксису Java, но не идентичен.

Каждый действительный код Java также является допустимым кодом Groovy, но не обязательно с тем же значением, которое вы видите в этом примере.

Другие вопросы по тегам