You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rhino中Java对象层级与JavaScript原型关联问题咨询

Rhino中Java父类属性访问问题的分析与解决

刚好我对Rhino的Java-JS交互这块比较熟悉,来给你拆解下你遇到的这个问题~

先还原下你的场景

你有这样的Java类层级:

// 父类
public class Parent {
    private String parentProperty;
    // 对应的getter、setter我就省略啦
}

// 子类,继承Parent
public class Child extends Parent {
    private String childProperty;
    // 同样省略getter、setter
}

为了把Child实例暴露给JavaScript表达式使用,你写了两个对应的宿主Scriptable类:

public class ParentScriptable extends ScriptableObject {
    private Parent parent;

    @Override
    public String getClassName() {
        return "Parent";
    }

    // 处理parentProperty的获取逻辑
    @Override
    public Object get(String name, Scriptable start) {
        if ("parentProperty".equals(name)) {
            return parent.getParentProperty();
        }
        return super.get(name, start);
    }
}

public class ChildScriptable extends ScriptableObject {
    private Child child;

    @Override
    public String getClassName() {
        return "Child";
    }

    // 处理childProperty的获取逻辑
    @Override
    public Object get(String name, Scriptable start) {
        if ("childProperty".equals(name)) {
            return child.getChildProperty();
        }
        return super.get(name, start);
    }
}

然后你把这两个Scriptable注册到Rhino,还在全局作用域里暴露了Child实例:

Context ctx = Context.enter();
try {
    Scriptable globalScope = ctx.initStandardObjects();

    // 注册两个Scriptable类
    ScriptableObject.defineClass(globalScope, ParentScriptable.class);
    ScriptableObject.defineClass(globalScope, ChildScriptable.class);

    // 创建并初始化Child实例
    Child child = new Child();
    child.setParentProperty("我是父类属性");
    child.setChildProperty("我是子类属性");
    ChildScriptable childScriptable = new ChildScriptable();
    childScriptable.child = child;

    // 把child对象暴露到JS全局作用域
    globalScope.put("child", globalScope, childScriptable);

    // 执行JS代码测试
    String jsCode = "print(child.childProperty); print(child.parentProperty);";
    ctx.evaluateString(globalScope, jsCode, "测试脚本", 1, null);
} finally {
    Context.exit();
}

结果你发现:如果不调用setPrototype方法,child.childProperty能正常访问,但child.parentProperty就返回undefined——这其实也符合预期,但咱们得解决这个问题对吧?

为啥会出现这个情况?

Rhino的Scriptable体系是靠原型链来实现属性查找的,和JavaScript原生的原型继承逻辑一模一样。当你在JS里访问一个对象的属性时,Rhino会先在当前Scriptable实例里找;找不到的话,就会顺着原型链往上找父类对应的Scriptable。

而你现在的代码里,ChildScriptable和ParentScriptable是完全独立的两个Scriptable类,没有建立任何原型关联。所以当JS找child.parentProperty时,只会在ChildScriptable里搜,搜不到就直接返回undefined,根本不会去ParentScriptable里找。

怎么解决?

核心就是给ChildScriptable设置原型为ParentScriptable的实例,让属性查找能顺着原型链走到父类的Scriptable里。

方式一:手动设置原型

修改你的实例化代码,加上setPrototype这一步:

Context ctx = Context.enter();
try {
    Scriptable globalScope = ctx.initStandardObjects();

    ScriptableObject.defineClass(globalScope, ParentScriptable.class);
    ScriptableObject.defineClass(globalScope, ChildScriptable.class);

    // 先创建父类的Scriptable实例作为原型
    Parent parent = new Parent();
    ParentScriptable parentScriptable = new ParentScriptable();
    parentScriptable.parent = parent;

    // 创建子类Scriptable并设置原型
    Child child = new Child();
    child.setParentProperty("我是父类属性");
    child.setChildProperty("我是子类属性");
    ChildScriptable childScriptable = new ChildScriptable();
    childScriptable.child = child;
    // 关键代码:把父类Scriptable设为子类的原型
    childScriptable.setPrototype(parentScriptable);

    globalScope.put("child", globalScope, childScriptable);

    String jsCode = "print(child.childProperty); print(child.parentProperty);";
    ctx.evaluateString(globalScope, jsCode, "测试脚本", 1, null);
} finally {
    Context.exit();
}

这样改完之后,JS访问child.parentProperty时,在ChildScriptable里找不到,就会去它的原型(ParentScriptable)里找,就能正确拿到父类属性的值了。

方式二:在子类Scriptable构造器里自动设置原型

如果你不想每次实例化都手动写setPrototype,可以在ChildScriptable的构造器里自动处理:

public class ChildScriptable extends ScriptableObject {
    private Child child;

    public ChildScriptable() {
        // 从全局作用域拿到Parent的原型对象
        Scriptable globalScope = getParentScope();
        Scriptable parentPrototype = (Scriptable) globalScope.get("Parent", globalScope);
        // 自动设置原型
        setPrototype(parentPrototype);
    }

    @Override
    public String getClassName() {
        return "Child";
    }

    @Override
    public Object get(String name, Scriptable start) {
        if ("childProperty".equals(name)) {
            return child.getChildProperty();
        }
        return super.get(name, start);
    }
}

这种方式利用了Rhino注册类时,会把类的构造器放到全局作用域的特性,自动获取父类的原型,让子类Scriptable自动继承父类的属性查找能力,用起来更省心。

内容的提问来源于stack exchange,提问作者Jan Moravec

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:35:48