Rhino:Java 数字不像 Java 脚本数字

Rhino: Java numbers don't behave like Javascript numbers

我有一个 Java class 的实例可以在我的 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)

我创建了一个值包装器:

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);

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

这是 Rhino 的一个奇怪功能:Java Number 设置为 engine.put("one", new Double(1)) 可以正常工作,而 Java 方法的结果取决于 return 方法本身声明的类型,通过反射读取 API:

  • 如果是原语,比如double,它会被转换成Java脚本编号
  • 否则它会像其他宿主对象一样处理,+ 表示串联,就像您的示例中的 Object 以及 Double

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

来自live Javadoc of WrapFactory.isJavaPrimitiveWrap()

By default the method returns true to indicate that instances of String, Number, Boolean and Character should be wrapped as any other Java object and scripts can access any Java method available in these objects

因此您可以将此标志设置为 false 以指示 Java Number 应转换为 Java 脚本数字。只需两行代码

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

这里是 the Gist 我用来测试的完整代码