Your Java version of SimpleClass should do two things. One, keep a private long value that stores the value of the C++ pointer to the backing native object (you might have to use BigInteger depending on how large a native pointer can be - unsigned long long?). Two, make the public methods (e.g. setIntVal
) native.
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();
}
Then implement those native methods in your JNI code. The initNativeSimpleClass()
method will new a backing C++ instance of SimpleClass
. The destroyNativeSimpleClass()
method will then delete that instance. The accessor methods will use the value of nativePtr
, cast it to a real pointer, and perform the appropriate operations on the native backing instance.
This idiom poses a real risk of leaking memory because users of the class MUST call destroy when they are done with an instance. If they don't, the backing native instance may not be properly destroyed. You can, as I've shown in the example, override finalize
to call the native destroyer function, but all the caveats about how finalize can't be relied on still apply. By setting the nativePtr
value to 0 upon destruction you avoid seg faults if destroy is called multiple times (it's safe in C++ to delete NULL).