将 Java Integer 绑定到 JavaScriptEngine 不起作用

发布于 2024-11-16 17:48:47 字数 909 浏览 3 评论 0原文

为了了解如何将 Java 对象绑定到动态语言中的符号,我编写了以下 spike 测试,将 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,但事实并非如此),但不会执行增量操作 i++原始的 Integer 对象。

有什么解释吗?

To see how binding Java objects to symbols in a dynamic language works, I wrote the following spike test, binding a java.lang.Integer to the symbol i to be changed in JavaScript:

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

    }

Unfortunately, I get a failure.

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

JavaScript knows the symbol i (otherwise it would have thrown a ScriptException which is not the case), but the increment operation i++ is not performed on the original Integer object.

Any explanations?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

盗心人 2024-11-23 17:48:47

我不知道 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

I don't know JavaSrciptEngine but Integer is immutable, thus you'd still have the previous value after changing it i JavaScript.

Edit: removing autoboxing to make it more clear:

It's much like this:

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
时光清浅 2024-11-23 17:48:47

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

Integer i = new Integer(17);

此行创建了两件事:

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

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

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

engine.put( "i", i );

此行将 i(这是对 Integer 对象的引用)传递给方法 put()。这意味着put知道Integer对象,而知道变量 。而且由于 Integer 对象本身无法修改,因此该方法(或任何其他方法)无法影响本地变量 我。

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

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

assertEquals( 18, i.intValue() );      

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

Here's what's happening in your code:

Integer i = new Integer(17);

This line creates two things:

  • A local variable of type Integer with the name i.
  • A new Integer object with the value 17.

The local variable is initialized with a reference to the newly created Integer object (i.e. it points to the Integer object now).

The Integer object can't be modified, since it's immutable.
The local variable could be modified by assigning a new reference to it.

engine.put( "i", i );

This line passes the value of i (which is the reference to the Integer object) to the method put(). This means that put only knows of the Integer object and not of the variable i. And since the Integer object itself can't be modified, there is no way for the method (or any other method) to influence what is stored in the local variable i.

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

This method effectively manipulates the JavaScript variable i created by the line before. That variable was initialized with the value of the local Java variable i, but it is not the same as that local variable.

assertEquals( 18, i.intValue() );      

Here you check the value of the local Java variable i, which is unchanged. The only way you could make that assert correct is by assigning something else to i (e.g. i = new Integer(18)).

给我一枪 2024-11-23 17:48:47

感谢 Thomas 和 Joachim Sauer 指出问题是由于 java.lang.Integer 的不变性造成的。

该整数通过 engine.put() 映射到真正的 JavaScript 变量,并且可以像数字一样对待。如果您需要结果,请调用 engine.get() 将其传回 Java。这与 上的 List 示例不同http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/,其中 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 );        

    }

Thanks to Thomas and Joachim Sauer for pointing out that the problem was due to the immutability of java.lang.Integer.

The integer is mapped to a genuine JavaScript variable by engine.put() and can be treated like a number. If you need the result, call engine.get() to pass it back into Java. This is different to the List<String> example on http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ where a Java object is passed to the script, and its own methods (as defined in Java) are applied to it from within the script by callback to Java, using reflection.

It is an interesting detail that the Integer will be retrieved as Double from JavaScript, indicating that there really was a forward and backward mapping to a JavaScript data object.

Here is the passing test (leaving out the instantiation of engine which is still the same as in my question - I have extracted it in the meantime).

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

    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文