为什么eval类给出了从int到double的转换错误?

时间:2017-05-29 23:12:02

标签: java function eval scriptmanager integral

我正在尝试制作一个采用字符串公式的方法,并通过以非常小的间隔进行Riemann的求和来求解该公式的积分。我正在使用ScriptEngine和ScriptEngineManager类来评估函数(使用eval()方法)。出于某种原因,我收到了这个错误:

  

线程“main”中的异常java.lang.ClassCastException:java.lang.Integer无法强制转换为java.lang.Double       在sum.integral(sum.java:31)       在sum.main(sum.java:13)

import java.beans.Expression;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class sum {

    //testing method
    public static void main(String[] args) throws ScriptException {

        double x = integral("5*x^2",0,5);
        System.out.println(x);

    }

    public static double integral(String function, double lower, double upper) throws ScriptException
    {
        double total = 0;

        ScriptEngineManager mgr = new ScriptEngineManager();
        ScriptEngine engine = mgr.getEngineByName("JavaScript");

        //Solves function from upper to lower with a .001 interval, adding it to the total.
        for (double i = lower; i < upper; i+=.001)
        {
            //evaluates the interval
            engine.put("x",i);
            total += (double)engine.eval(function);
        }

        return total;
    }

}

2 个答案:

答案 0 :(得分:3)

Nashorn使用optimistic typing(因为JDK 8u40),所以当不需要双精度时它将使用整数。因此,你不能指望它返回Double。

此外,5*x^2表示&#34;五倍x xor 2&#34;在JavaScript中。 **指数运算符是在较新版本的JavaScript语言中定义的,但Nashorn尚不支持它。

如果您将JavaScript代码更改为5*x*x,那么它会起作用,但这样做会更安全:

total += 0.001 * ((Number)engine.eval(function)).doubleValue();

编译常用代码

由于您在循环中重复调用此函数,因此最佳做法是提前编译该函数。这种性能优化并不是绝对必要的,但因为引擎必须每次都编译你的函数(虽然它可能使用缓存来帮助它)。

import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptContext;

CompiledScript compiledScript = ((Compilable)engine)
    .compile("function func(x) { return " + function + "}");
compiledScript.eval(compiledScript.getEngine()
    .getBindings(ScriptContext.ENGINE_SCOPE));

Invocable funcEngine = (Invocable) compiledScript.getEngine();

// . . .

total += 0.001 * ((Number)funcEngine.invokeFunction("func", i)).doubleValue();

使用ES6语言功能

将来,当Nashorn支持**运营商时,如果您想使用它,您可能需要打开这样的ES6功能:

import jdk.nashorn.api.scripting.NashornScriptEngineFactory;

NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine enjin = factory.getScriptEngine("--language=es6");

或者像这样:

java -Dnashorn.args=--language=es6

*编辑以考虑评论中指出的数学修正。

答案 1 :(得分:2)

您的JS代码段返回Integer(*),因为x^2不是在JavaScript中获得2的幂的正确方法。请改为5*Math.pow(x,2),表达式将返回Double

在JavaScript中,^运算符为bitwise XOR

计算积分的循环也是错误的,你需要乘以矩形宽度:

    double delta = 0.001;
    for (double i = lower; i < upper; i += delta) {
        //evaluates the interval
        engine.put("x", i);
        total += delta * ((Number) engine.eval(function)).doubleValue();
    }

(*)有关初步解释,请参阅David's answer。但在评论中,@ A.Sundararajan提供了反对此的证据。我没有调查确切的原因,我只观察到我得到了Integer,并且只是猜测在表达式中使用按位运算(来自OP的原始代码)触发了转换为整数。我最初编辑我的帖子以包含“数学错误”的修复,但大卫的新答案(大约4分钟^^)对于原始问题更完整,并且应该仍然是接受的答案恕我直言。