使用JDK动态代理实现equals()方法

6

这是我第一次使用标准JDK动态代理来实现自己的代理类。它运行得相当不错,但有一个细节需要注意:equals(...)方法。

假设我们有一个简单的接口,我们想要进行代理:

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

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

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

这也在这篇文章中讨论过。

我的问题是:如果我希望所有四种“equals”情况都返回true,最好(即最安全和最不具 invasive性)的方法是什么?


2
你需要根据接口的成员方法来实现接口实现类的equals方法。可以参考List#equals(Object)方法的实现方式(以及在javadoc中的描述)。 - Sotirios Delimanolis
2个回答

1
这是一个可能的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
你会找到一个可行的解决方案。但是有一个巨大的问题:
需要将要比较的两个对象都意识到代理包装。
在技术上,JAVA在处理代理时与其他对象一样正确。但是......
我个人的看法是,当我遇到这个问题时,现在仍然是:JAVA应该引入内置支持代理,以便在调用哈希码和等于时立即取消包装。
代理应该对“正常”实现透明。您不应该担心您是否具有代理或原始对象。但是JAVA在这方面做得不对。
一种可能性是使两个对象都意识到代理包装并在equals/hashcode中处理它。但这会使原始类淹没在不应该有的依赖关系中。
另一种可能性是如果不需要代理行为,则取消代理并使用真实对象。在创建代理的上下文中,您应该有一个像这样的映射表:
Map<Proxy, Original> map;

你不应该传递JAVA代理。每个对象都必须知道,代理已经被传递进来了,因为它们可能会将其存储在Set实现中,其中激活了equals和hashcode。一旦你传递JAVA代理,你就会污染使用这个依赖的类。
Java代理应该尽可能地隔离。产生Java代理的类应该是唯一使用它们的类。
另一个(更多样板化的)可能性是使用标准的代理模式而没有任何JAVA代理。但是在这里,如果你传递你的代理对象,你也必须考虑在hashcode/equals中进行代理包装。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接