你所忽略的,覆盖equals时需要注意的事项《effective java》
我們都知道Object的equals的比較其實(shí)就是==的比較,其實(shí)是內(nèi)存中的存放地址的比較。正常邏輯上:類的每個(gè)實(shí)例本質(zhì)上都是唯一的。
在工作中我們實(shí)際的業(yè)務(wù)邏輯往往有可能出現(xiàn)一些相對特殊的需求需要對equals方法進(jìn)行重寫,那么重寫equals需要注意哪些規(guī)則或者通用的約定呢?
?
equals方法實(shí)現(xiàn)了等價(jià)關(guān)系(equivalence relation):
- 自反性(reflexive)。對于任何非null的引用值x,x.equals(x)必須返回true。
- 對稱性(symmetric)。對于任何非null的引用值x和y,當(dāng)且僅當(dāng)y.equals(x)返回true時(shí),x.equals(y)必須返回true。
- 傳遞性(transitive)。對于任何非null的引用值x、y和z。如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必須返回true。
- 一致性(consistent)。對于任何非null的引用值x和y,只要equals的比較操作在對象中所用的信息沒有被修改,多次調(diào)用x.equals(x)就會一致地返回true,或者一致的返回false。
- 對于任何非null的引用值x,x.equals(null)必須返回false。
第一點(diǎn)自反性不需要多說,基本上不可能會出現(xiàn)違背這條約定的情況當(dāng)自己和自己比較的時(shí)候返回false。
第二點(diǎn)對稱性,這種情況還是有可能會出現(xiàn)的。我們可以假設(shè)一個(gè)場景,這個(gè)場景是我們創(chuàng)建一個(gè)類并且里面只有一個(gè)String屬性字段,這個(gè)類需要實(shí)現(xiàn)的是可以不區(qū)分字符串的大小寫。
pubilc final class IgnoreCaseString {private final String s;public IgnoreCaseString(String s) {if (s == null)throw new NullPointerException();this.s = s;}@Overridepublic boolean equals(Object o) {if (o instanceof IgnoreCaseString)return s.equalsIgnoreCase(((IgnoreCaseString) o).s);if (o instanceof String)return s.equalsIgnoreCase((String) o);return false;}...//更多代碼(重寫equals就需要重寫hashCode) }
在這個(gè)類中,equals方法的意圖非常好,它的企圖是可以與普通的字符串對象進(jìn)行互操作。但是這段代碼無意中觸犯了對稱性這個(gè)約定,從new?IgnoreCaseString(“Po”).equals("po")是為true的,但是反過來“po”.equals(new IgnoreCaseString("Po"))的結(jié)果是false。假如違反了這一情況而沒有去更正,他會破壞已有的集合框架的一些方法,使其變的不在準(zhǔn)確。
IgnoreCaseString ics = new IgnoreCaseString("Po");List<IgnoreCaseString> list = new ArrayList<IgnoreCaseString>();list.add(ics);System.out.println(list.contains("po"));List<String> list1 = new ArrayList<>();list1.add("po");System.out.println(list1.contains(ics));結(jié)果是,此時(shí)list.contains(s)會返回什么結(jié)果呢?沒人知道,在Sun的當(dāng)前實(shí)現(xiàn)中,它碰巧返回false,但這只是這個(gè)特定實(shí)現(xiàn)得出的結(jié)果而已。在其他的實(shí)現(xiàn)中,它有可能返回true(如上面代碼中的list1.contains(ics)),或者拋出一個(gè)運(yùn)行時(shí)(runtime)異常。一旦違反了equals約定,當(dāng)其他對象面對你的對象時(shí),你完全不知道這些對象的行為會這么樣。
第三點(diǎn)傳遞性,equals約定:如果一個(gè)對象等于第二個(gè)對象,并且第二個(gè)對象又等于第三個(gè)對象,則第一個(gè)對象一定等于第三個(gè)對象。無意識違反這一情況其實(shí)不難想象,考慮子類的情形,子類增加的信息會影響到equals的比較結(jié)果。
public class TwoDCoordinate {private final int x;private final int y;public TwoDCoordinate(int x, int y) {this.x = x;this.y = y;}@Overridepublic boolean equals(Object o) {if (!(o instanceof TwoDCoordinate))return false;TwoDCoordinate p = (TwoDCoordinate)o;return p.x == x && p.y == y;}...//更多代碼(重寫equals就需要重寫hashCode) }
public class ThreeDCoordinate extends TwoDCoordinate{
private final int z;
public ThreeDCoordinate(int x, int y, int z) {
super(x, y); this.z=z;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof TwoDCoordinate))
return false;
if (!(o instanceof ThreeDCoordinate))
return o.equals(this);
return super.equals(o) && this.z == ((ThreeDCoordinate) o).z;
}
...//更多代碼(重寫equals就需要重寫hashCode) } ThreeDCoordinate t1 = new ThreeDCoordinate(1, 2, 3);TwoDCoordinate t2 = new TwoDCoordinate(1, 2);ThreeDCoordinate t3 = new ThreeDCoordinate(1, 2, 4);System.out.println(t1.equals(t2));System.out.println(t2.equals(t1));System.out.println(t2.equals(t3));System.out.println(t1.equals(t3));
結(jié)果是true??true? ?true? ?false,上述代碼已經(jīng)滿足了自反性和對稱性的約定,但是沒有滿足傳遞性,t1.equals(t2)為true,t2.equals(t3)為true,t1.equals(t3)卻為false。
這種情況很多程序員會犯。
?想要避免這種情況,其實(shí)可以用getClass測試代替instanceof測試。如將TwoDCoordinate 的equals 方法改為
public boolean equals(Object o) {if (o == null || o.getClass() != this.getClass())return false;TwoDCoordinate p = (TwoDCoordinate)o;return p.x == x && p.y == y;}這種替代方式其實(shí)不會太糟糕,但是結(jié)果卻不會太理想,暫時(shí)沒有想到令人滿意的辦法實(shí)現(xiàn)既可以擴(kuò)展又不可實(shí)例化的類,但是可以考慮復(fù)合優(yōu)先于繼承。
第四點(diǎn)一致性,equals約定的第四個(gè)要求是,如果兩個(gè)對象相等,它們就必須始終保持相等,除非它們中有一個(gè)對象(或者兩個(gè)都)被修改了。換句話說,可變對象在不同的時(shí)候可以與不同的對象相等,而不可變對象則不會這樣。當(dāng)你在寫一個(gè)類的時(shí)候,應(yīng)該仔細(xì)考慮她是否應(yīng)該是不可變的。如果認(rèn)為它應(yīng)該是不可變的,就必須保證equals方法滿足這樣的限制條件:相等的對象永遠(yuǎn)相等,不相等的對象永遠(yuǎn)不相等。
?
轉(zhuǎn)載于:https://www.cnblogs.com/saoyou/p/10318517.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的你所忽略的,覆盖equals时需要注意的事项《effective java》的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【工具】js脚本下载百度文库生成word
- 下一篇: 编译器的2点优化