I am working on a Java API where many Java objects are indeed wrappers for equivalent C ++ objects. Java objects create C ++ objects and are responsible for releasing them when they are no longer needed. I am wondering how best to use this template, I see two possible options:
Build a C ++ object in the constructor using a call to the static native method and a final variable to hold the built-in descriptor.
public abstract class NativeBackedObject1 implements java.lang.AutoCloseable { protected final long _nativeHandle; protected final AtomicBoolean _nativeOwner; protected NativeBackedObject1(final long nativeHandle) { this._nativeHandle = nativeHandle; this._nativeOwner = new AtomicBoolean(true); } @Override public close() { if(_nativeOwner.copareAndSet(true, false)) { disposeInternal(); } } protected abstract void disposeInternal(); } public SomeFoo1 extends NativeBackendObject1 { public SomeFoo1() { super(newFoo()); } @Override protected final void disposeInternal() {
Build a C ++ object in the constructor using a call to the native native method, and not a finite variable to store the built-in descriptor.
public abstract class NativeBackedObject2 implements java.lang.AutoCloseable { protected long _nativeHandle; protected boolean _nativeOwner; protected NativeBackedObject2() { this._nativeHandle = 0; this._nativeOwner = true; } @Override public void close() { synchronized(this) { if(_nativeOwner && _nativeHandle != 0) { disposeInternal(); _nativeHandle = 0; _nativeOwner = false; } } } protected abstract void disposeInternal(); } public SomeFoo2 extends NativeBackendObject2 { public SomeFoo2() { super(); _nativeHandle = newFoo(); } @Override protected final void disposeInternal() {
At the moment, I think that (1) is a better approach because:
- a. This means that I can set
_nativeHandle unchanged ( final ). So I don’t need to worry about concurrent access to it or about unexpected changes (the code is actually more complex than these simplified examples). - b. Due to the constructor, I formalized in the design that any subclass of the
NativeBackedObject class is the owner of its corresponding native object (represented by _nativeHandle ), since it cannot be built without it.
Are there any advantages of approach (2) to (1) or any problems with approach (1)?
I could also see an alternative pattern for approach (1), let it fit (3):
public abstract class NativeBackedObject3 implements java.lang.AutoCloseable { protected final long _nativeHandle; protected final AtomicBoolean _nativeOwner; protected NativeBackedObject3() { this._nativeHandle = newInternal(); this._nativeOwner = new AtomicBoolean(true); } @Override public close() { if(_nativeOwner.copareAndSet(true, false)) { disposeInternal(); } } protected abstract long newInternal(); protected abstract void disposeInternal(); } public SomeFoo3 extends NativeBackendObject3 { public SomeFoo3() { super(); } @Override protected final void disposeInternal() {
The advantage of (3) over (1) is that I can return to the default constructor, which can help in creating layouts for testing, etc. The main drawback, however, is that I can no longer pass additional parameters to newFoo() .
Perhaps there are other approaches that I skipped? Suggestions are welcome ...
source share