如何从 JavaScript 调用 Java 实例的方法?
我正在使用 Mozilla Rhino JavaScript 模拟器。它允许我将 Java 方法添加到上下文中,然后像调用 JavaScript 函数一样调用它们。但除非我使用静态方法,否则我无法让它工作。
问题出在文档的这一部分:
如果该方法不是静态的,则 Java 的“this”值将对应于 JavaScript 的“this”值。任何使用不正确的 Java 类型的“this”值调用函数的尝试都会导致错误。
显然,我的 Java“this”值与 JavaScript 中的值不对应,我不知道如何使它们对应。最后,我想在 Java 中创建一个实例,并在全局范围内安装其中的几个方法,这样我就可以从 Java 初始化该实例,但在我的脚本中使用它。
有人有一些示例代码吗?
I'm using the Mozilla Rhino JavaScript emulator. It allows me to add Java methods to a context and then call them as if they were JavaScript functions. But I can't get it to work except when I use a static method.
The problem is this part of the documentation:
If the method is not static, the Java 'this' value will correspond to the JavaScript 'this' value. Any attempt to call the function with a 'this' value that is not of the right Java type will result in an error.
Apparently, my Java "this" value doesn't correspond with the one in JavaScript and I have no idea how to make them correspond. In the end, I'd like to create an instance in Java, and install a couple of methods from it in the global scope, so I can initialize the instance from Java but use it in my scripts.
Does anyone have some example code for this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
当一个 java 方法(无论是静态还是非静态)要在某个作用域内作为全局函数使用时,我们使用以下逻辑:
这里
boundScope
应该始终是该函数所在的作用域可供使用。然而,父作用域的值取决于我们绑定的是实例方法还是静态方法。对于静态方法,它可以是任何有意义的范围。它甚至可以与
boundScope
相同。但对于实例方法,
parentScope
应该是其方法被绑定的实例。以上只是背景信息。现在我将解释问题是什么,并给出一个自然的解决方案,即允许将实例方法直接作为全局函数调用,而不是显式创建对象的实例,然后使用该实例调用该方法。
当调用函数时,Rhino 会调用
FunctionObject.call()
方法,该方法会传递对this
的引用。如果该函数是全局函数,则在不引用this
的情况下调用它(即xxx()
而不是this.xxx()
),传递给FunctionObject.call()
方法的this
变量的值是进行调用的范围(即在本例中为this
参数将与scope
参数的值相同)。如果被调用的 java 方法是实例方法,这就会成为一个问题,因为根据
FunctionObject
类的构造函数的 JavaDocs:如果该方法不是静态的,则 Java
this
值将对应于 JavaScriptthis
值。任何使用不正确的 Java 类型的this
值调用函数的尝试都会导致错误。在上面描述的场景中,情况正是如此。 javascript
this
值与 javathis
值不对应,并导致不兼容对象错误。解决方案是子类化
FunctionObject
,重写call()
方法,强制“修复”this
引用,然后让调用正常进行。所以类似:
我认为通过下面粘贴的独立/完整的示例可以最好地理解它。在此示例中,我们将实例方法:myJavaInstanceMethod(Double number) 公开为 javascript 作用域('scriptExecutionScope')内的全局函数。因此,在这种情况下,“parentScope”参数的值必须是包含此方法的类的实例(即 MyScriptable)。
如果您想查看修复后的行为,请取消注释第 78 行和注释行 79:
如果您想查看没有修复的行为,请注释第 78 行并取消注释行 79:
希望这会有所帮助。
When a java method (whether static or non-static) is to be made available as a global function within a scope we use the following logic:
Here the
boundScope
should always be the scope in which the function is to be made available.However the value of the parent scope depends on whether we are binding an instance method or static method. In case of a static method, it can be any scope that makes sense. It can even be the same as the
boundScope
.But in case of instance method, the
parentScope
should be the instance whose method is being bound.The above was just background info. Now I will explain what the issue is and give a natural solution for it i.e. one that allows invoking the instance method directly as a global function rather than explicitly creating an instance of the object and then invoking the method using that instance.
When a function is called, Rhino invokes the
FunctionObject.call()
method that is passed a reference tothis
. In case the function is a global function, it is called without a reference tothis
(i.e.xxx()
instead ofthis.xxx()
), the value of thethis
variable that gets passed to theFunctionObject.call()
method is the scope in which the call was made (i.e. in this case the value of thethis
parameter will be same as the value of thescope
parameter).This becomes a problem in case the java method being invoked is an instance method because as per the JavaDocs of constructor of
FunctionObject
class:If the method is not static, the Java
this
value will correspond to the JavaScriptthis
value. Any attempt to call the function with athis
value that is not of the right Java type will result in an error.And in the scenario described above that is exactly the case. The javascript
this
value does NOT correspond to the javathis
value and results in an incompatible object error.The solution is to subclass
FunctionObject
, override thecall()
method, forcefully 'fix' thethis
reference, and then let the call proceed normally.So something like:
I think it would be best understood with a self-contained/complete example pasted below. In this example, we are exposing the instance method: myJavaInstanceMethod(Double number) as a global function within a javascript scope ('scriptExecutionScope'). So in this case the value of the 'parentScope' parameter must be an instance of the class that contains this method (i.e. MyScriptable).
If you want to see the behavior WITH the fix then uncomment line 78 and comment line 79:
If you want to see the behavior WITHOUT the fix then comment line 78 and uncomment line 79:
Hope this helps.
您可以做的是将 Java 实例 绑定到 Javascript 上下文,然后从 Javascript 该标识符将是对“真实”Java 对象的引用。然后您可以使用它从 Javascript 到 Java 进行方法调用。
Java 端:
Javascript:
现在该示例假设您正在使用 JDK 6 java.util.script API 在 Java 和 Rhino 之间进行交互。与“普通”Rhino 略有不同,但基本思想是相同的。
或者,您可以将 Java 类导入到 Javascript 环境中,当您对 Java 类的引用使用 Javascript“new”时,Rhino 会为您提供对 Java 对象的 Javascript 域引用。
What you can do is to bind a Java instance to the Javascript context, and then from Javascript that identifier will be a reference to the "real" Java object. You can then use it to make method calls from Javascript to Java.
Java side:
Javascript:
Now that example assumes you're using the JDK 6 java.util.script APIs to get between Java and Rhino. From "plain" Rhino, it's a little different but the basic idea is the same.
Alternatively, you can import Java classes into the Javascript environment, and Rhino gives you Javascript-domain references to Java objects when you use Javascript "new" on references to Java classes.
@Jawad 的答案很好,但它仍然要求parentScope 是您尝试插入的对象的父级。如果要将全局函数添加到使用 initStandardObjects() 创建的作用域中,则必须使用共享作用域(这有点解释在文档中,但缺乏完整的示例)。
我是这样做的(这是 Android,所以请原谅 Kotlin):
@Jawad's answer is great, but it still requires the parentScope to be a parent of the object you're trying to insert. If you want to add global functions into the scope created with initStandardObjects(), you have to use shared scope (which is sort of explained in the docs but lacks a full example).
Here's how I did it (this is Android so please excuse the Kotlin):