Rhino:Java数字的行为与Javascript数字不同

时间:2021-09-27 21:06:34

I have an instance of this Java class accessible in my Javascript program

我在我的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;
  }
}

This is the code inside main():

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

The simple expression ctx.c(1) prints:

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

rhino:> 1.1 (java.lang.Double)

Now here is what happens with ctx.c(1) + ctx.c(2):

现在这里是ctx.c(1)+ ctx.c(2):

rhino:> 1.12.2 (java.lang.String)

And finally (ctx.c(1) + ctx.c(2)) * ctx.c(3):

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

rhino:> nan (java.lang.Double)

Rhino is performing string concatenation instead of number arithmetics! The following program works as expected instead:

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

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

Outputs:

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

2 个解决方案

#1


This is a strange feature of Rhino: a Java Number set with engine.put("one", new Double(1)) works as expected, while the result of a Java method depends on the return type declared by the method itself, which is read with the reflection API:

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

  • if it's a primitive, like double, it's converted to a Javascript number
  • 如果它是一个原语,如double,则转换为Javascript编号

  • otherwise it's handled like other host objects and the + means concatenation, either Object like in your sample as well as Double
  • 否则它像其他主机对象一样处理,+表示连接,无论是样本中的对象还是Double

You can configure this behavior with wrapFactory.setJavaPrimitiveWrap(false) on the WrapFactory in the current Context. This way the Rhino code can be kept in the bootstrap lines of your program and doesn't clutter ContentProvider (which I guess is some sort of configuration proxy)

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

From the live Javadoc of WrapFactory.isJavaPrimitiveWrap()

来自WrapFactory.isJavaPrimitiveWrap()的实时Javadoc

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

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

So you can set this flag to false to indicate that Java Number's should be converted to Javascript numbers. It takes just two lines of code

因此,您可以将此标志设置为false,以指示应将Java编号转换为Javascript编号。它只需要两行代码

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

Here is the Gist with the full code I used to test

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

#2


I created a value wrapper:

我创建了一个值包装器:

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

and an edit function:

和编辑功能:

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

Now the expression works as expected. Thanks all.

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

#1


This is a strange feature of Rhino: a Java Number set with engine.put("one", new Double(1)) works as expected, while the result of a Java method depends on the return type declared by the method itself, which is read with the reflection API:

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

  • if it's a primitive, like double, it's converted to a Javascript number
  • 如果它是一个原语,如double,则转换为Javascript编号

  • otherwise it's handled like other host objects and the + means concatenation, either Object like in your sample as well as Double
  • 否则它像其他主机对象一样处理,+表示连接,无论是样本中的对象还是Double

You can configure this behavior with wrapFactory.setJavaPrimitiveWrap(false) on the WrapFactory in the current Context. This way the Rhino code can be kept in the bootstrap lines of your program and doesn't clutter ContentProvider (which I guess is some sort of configuration proxy)

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

From the live Javadoc of WrapFactory.isJavaPrimitiveWrap()

来自WrapFactory.isJavaPrimitiveWrap()的实时Javadoc

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

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

So you can set this flag to false to indicate that Java Number's should be converted to Javascript numbers. It takes just two lines of code

因此,您可以将此标志设置为false,以指示应将Java编号转换为Javascript编号。它只需要两行代码

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

Here is the Gist with the full code I used to test

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

#2


I created a value wrapper:

我创建了一个值包装器:

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

and an edit function:

和编辑功能:

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

Now the expression works as expected. Thanks all.

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