I have a simple Java class hierarchy that looks like so:
public abstract class Parent
{
private String parentProperty = "parent property value";
public String getParentProperty()
{
return parentProperty;
}
}
public class Child
extends Parent
{
private String childProperty = "child property value";
public String getChildProperty()
{
return childProperty;
}
}
I have a Child instance that I want to expose in my JavaScript expressions. To make this happen, I created two host Scriptable classes that look like so:
public class ParentScriptable
extends ScriptableObject
{
private Parent parent;
public ParentScriptable()
{
// no-arg constructor that seems to be required by Rhino's defineClass
}
public ParentScriptable( NativeJavaObject wrapper )
{
parent = (Parent) wrapper.unwrap();
}
@JSGetter
public String getParentProperty()
{
return parent.getParentProperty();
}
@Override
public String getClassName()
{
return "Parent";
}
}
public class ChildScriptable
extends ScriptableObject
{
private Child child;
public ChildScriptable()
{
// no-arg constructor seems to be required by Rhino's defineClass
setPrototype( new ParentScriptable() );
}
public ChildScriptable( NativeJavaObject wrapper )
{
child = (Child) wrapper.unwrap();
setPrototype( new ParentScriptable( wrapper ) );
}
@JSGetter
public String getChildProperty()
{
return child.getChildProperty();
}
@Override
public String getClassName()
{
return "Child";
}
}
This is how I register the two Scriptable classes with Rhino and expose a Child instance in the global scope that is used to evaluate my JavaScript expression:
Context cx = Context.enter();
Scriptable globalScope = cx.initStandardObjects();
// register host classes with in global scope
ScriptableObject.defineClass( globalScope, ParentScriptable.class );
ScriptableObject.defineClass( globalScope, ChildScriptable.class );
// expose a Child instance in the global scope as its 'child' property
globalScope.put( "child", globalScope,
cx.newObject( globalScope, "Child", new Object[] { Context.toObject( new Child(), globalScope ) } ) );
// get value of "child.parentProperty"
Object jsResult = cx.evaluateString( globalScope, "child.parentProperty", "<expression>", 1, null );
Context.exit();
Without the "setPrototype" calls, I can access child.childProperty, but child.parentProperty is undefined. This is not surprising, however, I would expect setPrototype to fix this, but it broke it completely and I am now getting "TypeError: Cannot find default value for object." errors.
Any idea how to properly wire up Rhino JavaScript prototypes to make this work?
Answering my own question after a day spent with a debugger.
It turns out that Rhino can handle the Java inheritance and translate it onto the JavaScript prototype-based inheritance.
The changes I had to make are:
Make ChildScriptable extend ParentScriptable.
Remove setPrototype calls.
Make "public ChildScriptable(NativeJavaObject wrapper)" constructor invoke super(wrapper).
Use the following code to register the two scriptable classes. Note: the last boolean parameter instructs Rhino to perform the inheritance mapping.
ScriptableObject.defineClass( globalScope, ParentScriptable.class, false, true ); // optional
ScriptableObject.defineClass( globalScope, ChildScriptable.class, false, true );
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.