12

我在任何地方都没有看到这个(或者我只是简单地没有看到它)但是有没有办法使用 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++ 类非常大,我希望避免创建大量的包装方法......

如果不可能,那很好,只是希望节省几天的编码时间,哈哈

4

3 回答 3

16

您的 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()方法将新建一个支持 C++ 的SimpleClass. 然后该destroyNativeSimpleClass()方法将删除该实例。访问器方法将使用 的值nativePtr,将其转换为真正的指针,并在本机支持实例上执行适当的操作。

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

于 2012-04-18T15:58:35.237 回答
5

不,你不能。C++ 和 Java ABI 完全不同——一方面,c++ 没有定义一个。真的,c++ 有很多特性根本无法映射到 Java,这根本行不通。你认为 Java 会如何处理 c++ 模板?指向原语的指针?不是指针的对象?

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

于 2012-04-18T15:38:43.780 回答
0

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

于 2012-04-18T15:44:45.380 回答