Effective Java -- 重寫equals方法的通用約定(一)
阿新 • • 發佈:2019-01-27
equals()
方法是Object
類的一個非final
方法,在重寫改類的時候我們應該遵循一下五種通用約定:
- 自反性:對於任何的非
null
的引用值x
,那麼x.equals(x)
的返回值一定為true
。 - 對稱性:對於任何的非空的引用值
x
和y
,則x.equals(y)
和y.equals(x)
的返回值是同真同假的。 - 傳遞性:對於任何非空的引用值
x
,y
和z
,如果x.equals(y)
的返回值為true
,並且y.equals(z)
的返回值也是true
,那麼對應的有x.equals(z)
的值也必須為true
。 - 一致性:對於任何非空的應用值
x
和y
,只要x.equals(y)
true
(或false
),那麼在沒有修改的情況下,無論呼叫多少次,其結果依然是true
(或false
)。 - 對於任何非空的引用值
x
,那麼有x.equals(null)
的返回值就一定是false
。
今天在這裡我們就著重看一下重寫equals()
方法時候要滿足的性質——對稱性
總所周知,在Linux的檔案系統中的檔名稱是區分大小寫的,而在Windows的檔案系統中的檔名是不區分大小寫的,這也就是說abc
和 AbC
是相等的。
所以我們現在需要一個儲存windows作業系統檔名稱的類,這就是說,其對應的判斷檔名的方法是忽略大小寫的。
package com.blog.effective.note8part1;
/**
* 〈windows作業系統的檔名〉<br>
*
* @author 未緒
* @time 2017/12/17 9:18
*/
public class WindowsFileName {
private String fileName; //檔名稱
public WindowsFileName(String fileName) {
this.fileName = fileName;
}
@Override
public boolean equals(Object obj) {
//如果穿傳過來的是 WindowsFileName
if(obj instanceof WindowsFileName){
return fileName.equalsIgnoreCase(((WindowsFileName) obj).fileName);
}
//如果穿過來的是檔名
if(obj instanceof String){
return fileName.equalsIgnoreCase((String)obj);
}
return false;
}
}
測試一下其是否滿足傳遞性
package com.blog.effective.note8part1;
/**
* 〈覆equals方法的時候應該注意的問題〉<br>
*
* @author 未緒
* @time 2017/12/17 8:32
*/
public class MyEqualsDemo1 {
public static void main(String[] args) {
WindowsFileName windowsFileName=new WindowsFileName("F:\\test");
String fileName="F:\\TEST";
System.out.println(windowsFileName.equals(fileName)); //true
System.out.println(fileName.equals(windowsFileName)); //false
}
}
可知該方法是違反了傳遞性的。
那麼這樣寫有什麼危害呢?
如下的例子,如果我們想將windowsFileName
和 fileName
同時加入到ArrayList
中儲存起來,這時候就會發現我們在WindowsFileName
中重寫的equals()
方法惹了大麻煩了。
如下程式碼輸出false
List<Object> list=new ArrayList<>();
list.add(windowsFileName);
System.out.println(list.contains(fileName));//fasle
而如果我們改變插入的順序,則結果就變得不同了,如下會輸出true
:
List<Object> list=new ArrayList<>();
list.add(fileName);
System.out.println(list.contains(windowsFileName));//true
這樣當然不是我們想要的結果,程式就會變得非常的不穩定,系統可能會莫名其妙的報錯。
我們將上面的WindowsFileName
類中重寫的 equals()
方法稍微修改一下,這樣就可以滿足傳遞性了:
@Override
public boolean equals(Object obj) {
//如果穿傳過來的是 WindowsFileName
if(obj instanceof WindowsFileName){
return fileName.equalsIgnoreCase(((WindowsFileName) obj).fileName);
}
return false;
}
以上就是有關重寫Object
類中的 equals()
方法要滿足傳遞性的相關內容了。
注意:這裡和是否滿足大小寫沒有啥關係,主要是重寫的equals()
方法可以接受String
型別的引數使其違反了傳遞性。