【问题标题】:Binding a Java Integer to JavaScriptEngine doesn't work将 Java 整数绑定到 JavaScriptEngine 不起作用
【发布时间】:2011-06-24 07:30:20
【问题描述】:

为了了解如何将 Java 对象绑定到动态语言中的符号,我编写了以下 spike test,将 java.lang.Integer 绑定到要在 JavaScript 中更改的符号 i

  @Test 
  public void bindToLocalVariable() throws ScriptException {

    javax.script.ScriptEngineManager sem 
       = new javax.script.ScriptEngineManager();
    javax.script.ScriptEngine engine 
       = sem.getEngineByName("JavaScript");    

    Integer i = new Integer(17);

    engine.put( "i", i );        
    engine.eval( "i++;" );  // Now execute JavaScript

    assertEquals( 18, i.intValue() );        

    }

不幸的是,我失败了。

java.lang.AssertionError: expected:<18> but was:<17>

JavaScript 知道符号 i(否则它会抛出 ScriptException,但情况并非如此),但不会对原始 Integer 对象执行递增操作 i++

有什么解释吗?

【问题讨论】:

  • 使用engine.get("i") 在您评估"i++;" 之后 获取引擎中i 的值。请记住,您现在有 两个 名为 i 的东西:Integer 应用程序中的本地变量 Integer 和 JavaScript 引擎中的变量。
  • Joachim,我没有两件事,但我为一件事有两个名字。一份来自 Java,一份来自 JavaScript。
  • @rplantiko:,那是错误的。你甚至观察到事实并非如此。 没有办法 JavaScript 引擎可以更改 i 的值(它也不能更改存储在 Integer 对象中的值,因为 Integer 是不可变的)。
  • 约希姆。至于整数的不变性,我希望在幕后自动装箱,就像 Java 语句 i++; 一样,它也会改变 i 的值。
  • Joachim:但感谢您评论的要点。我不知道JS不是在原件上工作,而是在副本上。

标签: java javascript rhino


【解决方案1】:

我不知道 JavaSrciptEngine,但 Integer 是不可变的,因此在将其更改为 JavaScript 后,您仍然拥有以前的值。

编辑:删除自动装箱以使其更清晰:

很像这样:

Integer i = new Integer(17);
Integer j = i;  //should be similar to the assignment in Javascript
j++; //the change to j is not reflected in i

System.out.println(i); //prints 17 not 18

【讨论】:

  • @Jesper,你是对的。我不应该在这里扔自动装箱 :) 我忘记了自动装箱也会将分配添加到新的 Integer 对象。
【解决方案2】:

以下是您的代码中发生的情况:

Integer i = new Integer(17);

这一行创建了两件事:

  • Integer 类型的局部变量,名称为 i
  • 一个新的Integer 对象,其值为17

使用对新创建的Integer 对象的引用来初始化局部变量(即它现在指向Integer 对象)。

Integer 对象不能修改,因为它是不可变的。
局部变量可以通过分配一个新的引用来修改。

engine.put( "i", i );

这一行将i(它是对Integer 对象的引用)传递给方法put()。这意味着put知道Integer对象,知道变量i。而且由于 Integer 对象本身无法修改,因此该方法(或任何其他方法)无法影响存储在局部变量 i 中的内容。

engine.eval( "i++;" );  // Now execute JavaScript

此方法有效地操作了前一行创建的 JavaScript 变量 i。该变量初始化为本地Java变量i的值,但它与该本地变量不同

assertEquals( 18, i.intValue() );      

在这里检查本地Java 变量i 的值,它没有改变。 唯一可以使该断言正确的方法是将其他内容分配给i(例如i = new Integer(18))。

【讨论】:

    【解决方案3】:

    感谢 Thomas 和 Joachim Sauer 指出问题出在 java.lang.Integer 的不变性上。

    整数通过engine.put() 映射到真正的JavaScript 变量,可以像数字一样对待。如果您需要结果,请调用 engine.get() 将其传递回 Java。这与http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ 上的List&lt;String&gt; 示例不同,在该示例中,Java 对象被传递给脚本,它自己的方法(如 Java 中定义的那样)通过回调从脚本中应用到它到Java,使用反射。

    一个有趣的细节是 Integer 将从 JavaScript 中检索为 Double,这表明确实存在到 JavaScript 数据对象的前向和后向映射。

    这是通过测试(省略了 engine 的实例化,这仍然与我的问题相同 - 同时我已经提取了它)。

      @Test 
      public void bindToInteger() throws ScriptException {
    
        Integer i = 17;
    
        engine.put( "i", i );        
        engine.eval( "i++" );  // Execute JavaScript
    
      // i is mapped back to java.lang.Double by get()
        double f = (Double) engine.get("i");  
        assertEquals( 18,  (int) f );        
    
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-01
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      • 2020-10-22
      • 2014-11-02
      相关资源
      最近更新 更多