我已经定义了一个接口RecordVisitor
,声明如下:
public interface RecordVisitor<K, V, Result> {
public Result visit(Record<K, V> rec);
}
我们的想法是使用Record对象调用visit
的实现,执行它们的逻辑并返回结果。我想阻止实现隐藏rec
值并在visit
调用之外使用它。
我认为这样做的唯一方法是向Record
添加状态,如果在记录对象上没有“活动”时调用任何方法,则抛出IllegalStateException
。然后我会将可怕的警告写入javadoc,并希望实施者阅读它。
是否有更强大的方法可以防止在Record
方法之外使用visit
对象?如果可能的话,我想建立一个接口,如果合同被违反,会导致编译时错误。
答案 0 :(得分:3)
作为visit
实现的一部分,无法阻止实现者存储对象的引用。
然而,这里有一个更大的哲学问题。如果您不信任实现RecordVisitor
接口的代码,那么为什么要使用该代码?如果你这样做,那么如果该代码保持对对象的引用,为什么会出现问题呢?
我认为这样做的唯一方法是向Record添加状态,如果在记录对象上没有“活动”时调用任何方法,则抛出IllegalStateException。
如果您不信任正在实施RecordVisitor
接口的程序员,这仍然是不够的。没有什么可以阻止实现者创建新方法
public void foo() {
this.rec.activate();
// Do something, and there is no IllegalStateException
this.rec.deactivate();
}
Record
的内容将显示为来自visit
内部的调用,但实际上不会来自visit
内部。
答案 1 :(得分:1)
首先,请听取@AdamMihalcin的建议。
现在,如果您仍想控制对Record
对象的访问权限,请查看Java的Proxy
类及其关联的InvocationHandler
。这允许您创建具有Record接口的代理对象(假设Record是一个接口)。然后,附加到代理的InvocationHandler
会将对接口上的方法的所有调用转发给真实的Record
对象。当对访问者的调用返回时,您可以在invalidate()
上调用InvocationHandler
之类的方法来指示它停止转发对真实Record对象的调用。
以下是InvocationHandler的示例。
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 ;
}
}