Overriding Proper Java equals method
我在Java类中重写了EDCOX1 0的方法,发现了一个我无法解释的难题。
标准equals()合同规定:
- 它是自反的:对于任何引用值x,x.equals(x)都应返回true。
- 它是对称的:对于任何引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)应返回true。
- 它是可传递的:对于任何引用值x、y和z,如果x.equals(y)返回真,y.equals(z)返回真,则x.equals(z)应返回真。
- 它是一致的:对于任何引用值x和y,多次调用x.equals(y)始终返回true或一致返回false,前提是不修改对象的equals比较中使用的信息。
- 对于任何非空引用值x,x.equals(null)应返回false。
因此,标准equals()方法的构造如下:
1 2 3 4 5 6 7
| public boolean equals (Object other ) {
if (null == other ) return false;
if (this == other ) return true;
if (!(other instanceof MyClassName )) return false;
MyClassName that = (MyClassName ) other ;
return this. myMemberVariable. equals(that. name);
} |
考虑到类Foo和类Bar extends Foo都有成员变量String baz,Foo内的标准equals()方法如下:
1 2 3 4 5 6 7
| public boolean equals (Object other ) {
if (null == other ) return false;
if (this == other ) return true;
if (!(other instanceof Foo )) return false;
Foo that = (Foo ) other ;
return this. baz. equals(that. name);
} |
而在Bar中的标准equals()方法如下:
1 2 3 4 5 6 7
| public boolean equals (Object other ) {
if (null == other ) return false;
if (this == other ) return true;
if (!(other instanceof Bar )) return false;
Barthat = (Bar ) other ;
return this. baz. equals(that. name);
} |
如果我有对象Foo foo = new Foo("Test");和Bar bar = new Bar("Test");,我给foo.equals(bar)打电话,这将返回true。如果我叫bar.equals(foo)的话,根据equals()合同的对称条款,这是因为在Bar的equals()中,(!(other instanceof Bar))是true中,使Bar的equal()方法返回false中,不正确的,应该是true中,逻辑上(两个对象String baz都等于eacx1〔5〕等于eacx1〔13〕中,使Bar的equal()方法返回EDOCX11〔22〕中,不正确的,按逻辑(两个对象String baz都等于eacx1〔5〕等于eacx1〔5〕等于eacx1〔13〕中,这是不正确一个ch other),根据对称性条款,如果x.equals(y),y.equals(x)本质上是true。
我知道getClass()对instanceof的论点,主张推翻equals(),不想再有这样的论点。
这就引出了我的实际问题。在这种情况下,如何正确覆盖遵循标准JAVA合同的EDCOX1 0方法?
- 答案在问题中。测试两个对象是否具有相同的类。
- 有一些简单的文章(javarevisited.blogspot.com/2011/02/…(artima.com/lejava/articles/equality.html)(http://www.artima‌&8203;.com/…(javapractices.com/topic/topication.do)?id=17)(http://hellip;但也不要忘记编写正确的hashcode()。
- @jb确定合同不要求它们是同一类的。在这种情况下,如果baz相同,那么预期用于Foo的op等于Bar。
- @那为什么还要在子类中重写equals()?
- @泰德霍普谁说他应该?作者自己说,如果一个Foo与一个Bar共用一个baz,那么它就相当于一个Bar。baz是父类的属性,为什么要在子类中重写equals()?
- @约翰:我同意。如果操作真的不想更改equals()的逻辑,他不应该重写它。我(也许天真地)认为他有充分的理由去推翻它。
- @约翰-我不是说他应该这样做;我是在问他为什么会这样做,考虑到他试图实现的语义。OP询问"如何正确覆盖…"。除非equals()被推翻,否则整个问题都是没有意义的。
- @Tedhop操作程序正在询问如何在不应该这样的情况下正确地重写equals方法。OP说,如果FOO和BAR共享一个共同的BAZ,它们可以是平等的。如果foo可以等于baz,那么foo最多只能用相同的实现重写equals。为什么用相同的实现重写一个实现?
- @约翰-这就是我评论的全部观点:当这是一种情况(显然)根本不应该被重写的时候,为什么要费心去重写等号呢?你最初的评论没有真正说明问题。
- @特德霍普,不要在一个指向行动的问题上给我贴标签!我们浪费了时间和精力来达成一致。这个问题显然是针对我的。
- 我不同意"近似复制"的投票。两个问题都涉及同一个主题,但是这个版本的问题有一个更详细的问题解释,这大大增加了我们的理解。
- @迟到了,谢谢。由于链接的"副本"与我的问题不同,因此我也不同意关闭,并且我有一个特定的理由来覆盖子类Bar中的equals()方法。
- 我投票决定重新开放。要重新打开它,您还需要另外四个人来做同样的事情:)
根据你的问题
- 如果Foo与baz相同,则Foo可以等于Bar。
- Foo与baz相同时,可以等于Foo。
- Bar与baz相同时,可以等于Bar。
这清楚地表明,Foo和Bar将具有与equals()完全相同的实现。因此,您根本不应该覆盖它。
如果你试图忽略这一点,并且无论如何都要覆盖它,你会得出一个明显的结论:你不能将Bar向下投射到Foo,但是你可以将这两个参数都投射到Bar。一旦你这样做,你就会意识到你可以简单地使用.equals(),从Bar。