我没有在任何地方看到这个(或者我可能很简单没有看到它)但有没有办法使用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
答案 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中所示。