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

Posted

技术标签:

【中文标题】Rhino:Java 数字的行为不像 Javascript 数字【英文标题】:Rhino: Java numbers don't behave like Javascript numbers 【发布时间】:2015-07-22 23:38:31 【问题描述】:

我的 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)

【问题讨论】:

天哪,居然有人正确同时使用了“java”和“javascript”标签! 但是为什么不试试 engine.eval("typeof ctx.c(1)") 看看 JavaScript 认为的类型是什么? typeof ctx.c(1) = object @lunicon 不错的解决方案!请将其添加为答案 (doesn't matter if you are the OP as well) 并接受。 @lunicon 你可能想看看我的新答案,因为它更容易编码和维护 【参考方案1】:

我创建了一个值包装器:

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

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

【讨论】:

【参考方案2】:

这是 Rhino 的一个奇怪特性:Java Numberengine.put("one", new Double(1)) 的集合按预期工作,而 Java 方法的结果取决于方法本身声明的返回类型,使用反射 API 读取:

如果是原始类型,例如double,则会转换为 Javascript 数字 否则它会像其他主机对象一样处理,+ 表示连接,Object 就像您的示例一样,Double

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

来自live Javadoc of WrapFactory.isJavaPrimitiveWrap()

默认情况下,该方法返回 true 以指示实例 字符串、数字、布尔值和字符应像任何其他值一样包装 Java 对象和脚本可以访问这些可用的任何 Java 方法 对象

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

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

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

【讨论】:

谢谢。它应该在文档中加粗。 同意。相反,我不得不调试和下载源代码。这似乎也是一个奇怪的默认值 我遇到了这个问题并搜索了所有文档但没有找到任何东西。谢谢你节省了我的时间。 更新:在 Rhino java7 中,它适用于每个 Number 类,bug 在最新的 Rhino 1.7.11 中,BigDecimal 和 BigInteger 没有包装!

以上是关于Rhino:Java 数字的行为不像 Javascript 数字的主要内容,如果未能解决你的问题,请参考以下文章

使用 Rhino(Mozilla 的 rhino)的优点

使用 require.js 和 Java/Rhino 解析模块

Rhino:如何从 Java 调用 JS 函数

Java 变量参数和 rhino

oralce不像Java,java中字符串+数字,能够得到结果字符串

用 Rhino 解释 Java 中的 JavaScript:暂停/恢复脚本