我可以使用JNI在Java代码中引用C ++对象吗?

时间:2012-04-18 15:34:39

标签: java c++ java-native-interface

我没有在任何地方看到这个(或者我可能很简单没有看到它)但有没有办法使用JNI返回c / c ++对象并在java中使用该对象?

例如(非常简单):

class simpleClass{
...
private:
int intVar;
public:
int getIntVar();
void setIntVar(int someNum);
...
}

在我的java代码中,我将如何执行以下操作:

...
simpleClass sc = new simpleClass();
sc.setIntVar(9);
System.out.println(sc.getIntVar());
...

我意识到这是一个非常简单的例子,但我只是在寻找概念 - 我在c ++中考虑的类是非常大的,我希望避免创建一个TON的包装器方法...

如果不可能那么好,只希望节省几天编码lol

3 个答案:

答案 0 :(得分:13)

您的Java版SimpleClass应该做两件事。一,保留一个私有long值,将C ++指针的值存储到后备本机对象(您可能必须使用BigInteger,具体取决于本机指针的大小 - unsigned long long?)。二,使公共方法(例如setIntVal)本地化。

public class SimpleClass {
    private long nativePtr;

    public SimpleClass() {
        nativePtr = initNativeSimpleClass();
    }

    public void destroy() {
        destroyNativeSimpleClass();
        nativePtr = 0L;
    }

    protected void finalize() throws Throwable {
        destroyNativeSimpleClass();
        nativePtr = 0L;
    }

    public native int getIntVal();
    public native void setIntVal(int val);

    private native long initNativeSimpleClass();
    private native void destroyNativeSimpleClass();
}

然后在JNI代码中实现这些本机方法。 initNativeSimpleClass()方法 new SimpleClass的支持C ++实例。然后,destroyNativeSimpleClass()方法将删除该实例。访问器方法将使用nativePtr的值,将其强制转换为实际指针,并在本机后备实例上执行适当的操作。

这个成语存在泄漏内存的真正风险,因为类的用户必须在完成实例时调用destroy。如果不这样做,则可能无法正确销毁后备本机实例。正如我在示例中所示,您可以覆盖finalize来调用本机驱逐舰函数,但是关于如何无法依赖finalize的所有警告仍然适用。通过在销毁时将nativePtr值设置为0,可以避免在多次调用destroy时出现seg错误(在C ++中安全删除NULL)。

答案 1 :(得分:6)

不,你不能。 C ++和Java ABI完全不同 - 例如,c ++没有定义一个。实际上,c ++有很多功能无法映射到Java,但这些功能无法正常工作。您如何期望Java能够处理c ++模板?对原始人的指针? 不是指针的对象?

现在你可以做的是,使用SWIG为你生成正确的包装器方法 - 这实际上可以工作,并且比你计划的工作多得多:)

答案 2 :(得分:0)

JNI仅为原始(或相当原始)类型定义其接口,以及内存缓冲区传递/管理功能。无法映射复杂的对象类型。但是,您可以通过编写自己的(单个)序列化/反序列化函数来实现此效果,如Returning a C++ class to Java via JNI中所示。