Rhino:Java 数字的行为不像 Javascript 数字

2022-09-02 22:32:35

我有一个可以在我的Javascript程序中访问的Java类的实例

public class ContentProvider {
  public Object c(int n) {
    switch (n) {
      case 1: return 1.1;
      case 2: return 2.2;
      case 3: return 3.3;
      case 4: return "4";
      case 5: return new java.util.Date();
    }
    return null;
  }
}

这是 main() 中的代码:

ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
engine.put("ctx", new ContentProvider());

res = engine.eval("ctx.c(1)");

System.out.printf("rhino:> %s (%s)%n"
        , res
        , res != null ? res.getClass().getName() : null
);

简单表达式打印:ctx.c(1)

rhino:> 1.1 (java.lang.Double)

现在这是发生的事情:ctx.c(1) + ctx.c(2)

rhino:> 1.12.2 (java.lang.String)

最后:(ctx.c(1) + ctx.c(2)) * ctx.c(3)

rhino:> nan (java.lang.Double)

Rhino正在执行字符串串联而不是数字算术!以下程序按预期方式工作:

engine.put("a", 1.1);
engine.put("b", 2.2);
engine.put("c", 3.3);
res = engine.eval("(a + b) * c");

输出:

rhino:> 10,89 (java.lang.Double)

答案 1

这是 Rhino 的一个奇怪功能:具有 Java 集的工作方式与预期相同,而 Java 方法的结果取决于该方法本身声明的返回类型,该类型是使用反射 API 读取的:Numberengine.put("one", new Double(1))

  • 如果它是一个原语,比如,它被转换为Javascript数字double
  • 否则,它将像其他宿主对象和均值串联一样进行处理,就像在示例中一样,以及+ObjectDouble

您可以使用 在 当前 中 配置此行为。这样,Rhino代码可以保存在程序的引导行中,并且不会混乱(我猜这是某种配置代理)wrapFactory.setJavaPrimitiveWrap(false)WrapFactoryContextContentProvider

来自 WrapFactory.isJavaPrimitiveWrap() 的 live Javadoc

默认情况下,该方法返回 true,以指示 String、Number、Boolean 和 Character 的实例应像任何其他 Java 对象一样进行包装,并且脚本可以访问这些对象中可用的任何 Java 方法。

所以你可以设置这个标志来指示Java应该转换为Javascript数字。它只需要两行代码falseNumber

Context ctx = Context.enter();
ctx.getWrapFactory().setJavaPrimitiveWrap(false);

以下是我用于测试的完整代码的要点


答案 2

我创建了一个值包装器:

public static class JSValue extends sun.org.mozilla.javascript.internal.ScriptableObject
{
    Object value;

    public JSValue(Object value) {
        this.value = value;
    }

    public String getClassName() {
        return value != null? value.getClass().getName(): null;
    }

    @Override
    public Object getDefaultValue(Class typeHint) {
        if (typeHint == null || Number.class.isAssignableFrom(typeHint)) {
            if (value instanceof Number)
                return ((Number) value).doubleValue();
        }

        return toString();
    }

    @Override
    public String toString() {
        return value != null? value.toString(): null;
    }
}

和编辑功能:

  public static class ContentProvider {
    public Object c(int n) {
    ... return new JSValue(1.1);

现在,表达式按预期方式工作。谢谢大家。