Java:防止在方法外部使用对象引用

时间:2012-03-14 19:12:12

标签: java

我已经定义了一个接口RecordVisitor,声明如下:

public interface RecordVisitor<K, V, Result> {
    public Result visit(Record<K, V> rec);
}

我们的想法是使用Record对象调用visit的实现,执行它们的逻辑并返回结果。我想阻止实现隐藏rec值并在visit调用之外使用它。

我认为这样做的唯一方法是向Record添加状态,如果在记录对象上没有“活动”时调用任何方法,则抛出IllegalStateException。然后我会将可怕的警告写入javadoc,并希望实施者阅读它。

是否有更强大的方法可以防止在Record方法之外使用visit对象?如果可能的话,我想建立一个接口,如果合同被违反,会导致编译时错误。

2 个答案:

答案 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 ;
    }
}