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, но не обязательно с тем же значением, которое вы видите в этом примере.