Java代理 -> 为什么代理对象和原始对象具有相同的哈希码?

5

我已经写了这个测试类,但我想知道为什么代理对象的hashCode与原始对象不同。有人知道原因吗?

public class Main {

public static void main(String[] args) {
    final Service realSubject = new Subject_A();
    final Service proxySubject = ProxyGenerator.makeProxy(Service.class, realSubject);
    final String hello = proxySubject.work("Hello");
    System.out.println("hello = " + hello);
    System.out.println("\n");
    System.out.println("realSubject: " + realSubject);
    System.out.println("proxySubject: " + proxySubject);
}
}

这是一个示例输出:
in Subject_A#work: str = Hello
hello = Hello_DONE


realSubject: at.me.proxy.Subject_A@4f4a7090
proxySubject: at.me.proxy.Subject_A@4f4a7090

2
你的代码中没有调用.hashCode(),所以我不明白你期望得到什么答案。请提供示例输出? - fge
ServiceSubject_AProxyGenerator是什么?它们来自某个库还是你自己编写的? - Codebender
1
可能是Java中覆盖equals和hashCode时应考虑哪些问题?的重复问题。 - Raedwald
如果委托了equals方法,就必须委托hashCode方法。 - Raedwald
1个回答

3

代理被用来间接访问底层对象,对于客户端代码而言,代理的存在应该是隐藏的。

通常这种模式被用在像 Spring 和 Hibernate 这样的框架中,为你的对象装饰上事务或安全能力。因此,代理对象的 hashcode()equals()toString() 输出应该和底层对象一致。

编辑

根据 @Holger 的更正进行更新。

首先,你观察到的是 toString() 调用的相同输出,而不是 hashcode()

通过代理实现 equals() 的方式比起一开始看起来要微妙一些。在典型的 equals() 实现中,对称性的属性将会违反 equals 合同:

对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 应该返回 true。

就是这样。

// works since you delegate same instance of wrapped class to underyling object
proxy.equals(wrapped); // true

但是
wrapped.equals(proxy); // false

由于:

 // proxy class != wrapped class 
 if (this.getClass() != obj.getClass()) {
        return false;
 }

正如@Holger所建议的那样,两个包装同一基础实例的代理可以相等而不违反对称性。

使代理等于包装实例以及反之的一个选项是通过界面成员(getter)实现等于状态的接口成员,并比较类与该接口。由于代理和基础对象都符合这个接口,它们将相等。


1
我反对委托equals的想法,因为这样做会违反对称性契约;代理对象会声称与原始对象相等,而原始对象通常不会与代理对象相等。但你指出了正确的方向:我们在这里看到的是toString()的委托在起作用,为原始对象和代理对象产生相同的输出,但在这段代码中并没有证明hashCode也会被委托。 - Holger
@Holger 我可能误解了:在代理实例上声明在java.lang.Object中的hashCode、equals或toString方法的调用将被编码并以与接口方法调用相同的方式分派到调用处理程序的invoke方法中,如上所述。来自[文档]:https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html。然而,在我看来,`equals()`、`hashcode()`和`toString()`被以相同的方式处理。 - John
它们都被委托给了InvocationHandler,这是正确的,但处理它们的方式取决于处理程序。让处理程序直接将equals调用委托给另一个对象并不是一个好主意。但是,您可以以两个包装相同对象的代理相等的方式实现它。这将符合契约。 - Holger

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