使用JDK Dynamic Proxies实现equals()

时间:2015-05-27 16:49:18

标签: java proxy equals dynamic-proxy

我有史以来第一次使用标准JDK动态代理实现自己的代理类。它的工作原理相当不错,除了一个细节:equals(...)方法。

我们假设我们有一个像这样的简单接口,我们要代理它:

public interface MyInterface {
    public String getID();
    public void setID(String id);
}

...我们的实现看起来像这样(生成hashCode()equals的标准Java Bean):

public class MyImplementation implements MyInterface {
    private String id;

    public String getID() { return this.id; }
    public void setID(String id) { this.id = id; }

    // hash code & equals generated by eclipse

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (this.databaseId == null ? 0 :      
        this.id.hashCode());
        return result;
    }

    public final boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (this.getClass() != obj.getClass()) {
            return false;
        }
        MyImplementation other = (MyImplementation) obj;
        if (this.databaseId == null) {
            if (other.databaseId != null) {
                return false;
            }
        } else if (!this.databaseId.equals(other.databaseId)) {
            return false;
        }
        return true;
    }
}

问题是,当我创建代理时,equals(...)方法不再是对称的:

original.equals(original); // true
proxy.equals(original);    // true, as the proxy forwards the call to the wrapped object
original.equals(proxy);    // false
proxy.equals(proxy);       // false

这也在this article中进行了讨论。

我的问题是:如果我希望所有四个“等于”的案例都能传递true,那么最好的(即最安全和最少侵入性)的方法是什么?

2 个答案:

答案 0 :(得分:0)

这是equals();

的可能替代方案
public final boolean equals(final Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (! (obj instanceof MyInterface)) // neither a Proxy nor a MyImplementation
    return false;

    MyInterface other = (MyInterface) obj;
    if (this.getID() == null) {
       if (other.getID() != null) {
               return false;
       }
    } else if (!this.getID().equals(other.getID())) {
      return false;
    }
    return true;
}

此代码使用getID()而不是直接访问该字段。到目前为止,它运作良好。

答案 1 :(得分:0)

您将找到一个可行的解决方案。但是存在一个很大的问题:

要相互比较的两个对象都必须知道Proxy-Wrapping。

JAVA在技术环境中做得很好,Proxies与其他对象的处理方式相同。但...

当我偶然发现这个问题时,我的个人意见现在就是:JAVA应该引入代理的内置支持,只要调用hashcode和equals,就会在内部解包。

代理应该对“正常”实现透明。你不应该为你有代理或原件而烦恼。但JAVA以这种方式做错了。

一种可能性是使两个对象都知道Proxy-Wrapping并在equals / hashcode中处理它。但是这会使原来的类充满了它不应该具有的依赖性。

另一种可能性是在不需要代理行为的情况下解包代理并改为使用真实对象。在您创建代理服务器的上下文中,您应该有这样的地图:

@angular

你不应该传递JAVA代理。每个对象都必须知道,代理被传入,因为它们可以将它们存储在Set-Implementations中,其中equals和hashcode被激活。一旦你绕过JAVA Proxies,就会污染使用具有这种依赖关系的类。

Java代理应该尽可能孤立。生成Java代理的类应该是唯一使用它们的类。

另一种(更多的是锅炉式)可能性是使用标准代理模式而没有任何JAVA代理。但是又来了:如果你传递代理对象,你必须考虑哈希码/等号中的代理包装。