Reputation: 5537
I've defined an interface RecordVisitor
, declared as follows:
public interface RecordVisitor<K, V, Result> {
public Result visit(Record<K, V> rec);
}
The idea is that implementations of visit
will get called with a Record object, do their logic, and return a result. I would like to prevent implementations from stashing away the rec
value and using it outside of the visit
invocation.
The only way I've thought to do this is by adding state to Record
, and throwing IllegalStateException
if any method is called on the record object while it is not "active". Then I would write dire warnings into the javadoc, and hope that implementers read it.
Is there a more robust way to prevent using the Record
object from being used outside of the visit
method? If possible, I'd like to build an interface that results in compile-time errors if the contract is violated.
Upvotes: 4
Views: 1519
Reputation: 2115
First, take the advice from @AdamMihalcin to heart.
Now, if you still want to control the access to the Record
object, take a look at Java's Proxy
class and its associated InvocationHandler
. This allows you to create a proxy object that has the Record interface (assuming Record is an interface). The InvocationHandler
attached to the Proxy would then forward all calls to methods on the interface to the real Record
object. When the call to the visitor returned, you can then call a method such as invalidate()
on your InvocationHandler
to instruct it to stop forwarding calls to the real Record object.
Here's an example of what the InvocationHandler might look like.
package test.proxy ;
import java.lang.reflect.InvocationHandler ;
import java.lang.reflect.Method ;
import java.lang.reflect.Proxy ;
public class CancelableObjectInvocationHandler
implements InvocationHandler
{
private Object _realObject ;
public CancelableObjectInvocationHandler ( Object realObject )
{
_realObject = realObject ;
}
public Object invoke ( Object proxy, Method method, Object[] args )
throws Throwable
{
Object ret = null ;
if ( method.getName ( ).equals ( "equals" ) )
{
// If we are invoking the equals method, we have to compare the
// Invocation Handlers since the Proxies forward the method call
boolean isEquals = true ;
if ( isEquals )
{
isEquals = ( args[0] instanceof Proxy ) ;
}
if ( isEquals )
{
Proxy otherProxy = (Proxy) args[0] ;
isEquals = this.equals ( Proxy.getInvocationHandler ( otherProxy ) ) ;
}
return new Boolean ( isEquals ) ;
}
else if ( null != _realObject )
{
// The object is active, so execute the method call.
ret = method.invoke ( _realObject, args ) ;
}
else
{
throw new IllegalStateException (
"Attempt to access an invalidated Object" ) ;
}
return ret ;
}
public Object getRealObject ( )
{
return _realObject ;
}
protected void invalidate ( )
{
_realObject = null ;
}
}
Upvotes: 1
Reputation: 14458
There is no way to prevent an implementor from storing a reference to an object as part of their implementation of visit
.
However, there is a bigger philosophical issue here. If you don't trust the code that implements the RecordVisitor
interface, then why are you using that code? If you do, then why is it a problem if that code keeps a reference to an object?
The only way I've thought to do this is by adding state to Record, and throwing IllegalStateException if any method is called on the record object while it is not "active".
If you don't trust the programmers who are implementing your RecordVisitor
interface, this still isn't enough. There's nothing to prevent the implementor from creating a new method
public void foo() {
this.rec.activate();
// Do something, and there is no IllegalStateException
this.rec.deactivate();
}
which to Record
will appear as an invocation from inside visit
but won't actually be from inside visit
.
Upvotes: 3