標籤:其他 獲得 沒有 end rri 特性 bool etag 內容
Object類中的equals方是用來判斷一個對象等於另一個對象,至於這個等於的條件需要,比如說,String類的equals相等的條件就是字串的內容必須相同,equals方法返回的值才為true。所以在我們在自己定義的類中,equals的重寫是常見的!這裡主要展示equals的特性和equals的正確寫法,至於equals方法具體的含義這裡不介紹!
1. 舉一個例子
在這介紹其他的,我們先來看看正確的寫法
public class Animal { private String name = null; public Animal(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object obj) { if(this == obj) { return true; } if(obj == null) { return false; } if(this.getClass() != obj.getClass()) { return false; } Animal animal = (Animal) obj; //return this.name.equals(animal.name); //這個方法只在JDK7及其以後才有的 //這個方法能夠保證兩個name其中只有一個為null的話,返回的false //如果兩個都為null的話,返回的是true //如果兩個都不為null的話,具體看情況 return Objects.equals(this.name, animal.name); }}
從上面的代碼中我們看到是,Animal類的equals方法判斷的相等條件是name是否為相同。說實話,判斷的條件非常的簡單,但是我們的代碼寫的非常複雜。可能有人會這樣寫的:
public boolean equals(Object obj) { if(! (obj instanceof Animal)) { return false; } Animal animal = (Animal) obj; return this.name.equals(animal.name); }
實際上,上面的代碼是有很大的問題,至於有什麼問題,待會再說!這裡我們來解釋一下正確equals方法寫的代碼:
1. this == obj, 毫無疑問,如果兩個對象的記憶體都是相同的話,那麼肯定是同一個對象了。2. this == null, 同樣的話,如果傳進來的對象是null的話,肯定為false。3. this.getClass == obj.getClass, 這個條件可能讓人有點疑惑。我來解釋一下,getClass是獲得當前的對象的Class對象,至於什麼是Class對象,這裡不解釋,需要的記得是:同一類的所有對象的獲得的Class對象都是同一個Class對象,也就是說,這裡的this如果obj不屬於同一個類的,那麼肯定為false。
那麼這種寫法有好處呢,等我把在列出一個類的代碼再說吧!
public class Dog extends Animal{ private int age = 0; public Dog(String name, int age) { super(name); this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object obj) { if(!super.equals(obj)) { return false; } Dog dog = (Dog) obj; return this.age == dog.age; }}
我們發現Dog類繼承於Animal類,並且重寫了父類的equals方法。在Dog類中equals方法,我們先來判斷了這兩個對象是否是相等的,其次再比較了age是否相同的。其中先調用了父類的equals來判斷,再來判斷子類本身的條件,這種方法有幾個好處:
1. 符合繼承的特性,若且唯若他們通過父類equals才有可能判斷子類的條件,因為如果不判斷父類的equals的話,當父類的equals返回的是false,但是通過調用子類的equals卻返回true。這個根本不符合物件導向的特性,實際點,如果兩個兒子老爸都不相同的話,兒子怎麼可能相同!2. 避免了obj是Dog類的子類(雖然這裡Dog類沒有子類)。因為這裡,我們先來調用父類的equals,父類的equals方法中:this.getClass() == obj.getClass 會協助我們判斷這兩個類是否是同一個類。按照我們以往的寫法,使用instanceof關鍵字來進行判斷的是有很大的問題的,如果this是Dog類的對象,但是obj的實際類型是Dog子類的對象,如果使用 obj instanceof Dog 這個返回的肯定為true,如果通過了我們這個判斷語句,最後來判斷我們的age。實際上,這兩個對象肯定不是同一個對象,所以最後用age來判斷是有問題的。至於這個問題的詳細解釋,待會還會提及!2. equals方法的特性
上面留了一些伏筆,這裡將詳細的解釋,但是在解釋之前,我們先來看看equals方法的特性:
1. 自反性:對於任何的非Null 物件,x.equals(x)返回肯定為true。2. 對稱性:對於任何兩個非Null 物件x、y,如果x.equals(y)返回的是true,那麼y.equals(x)返回的是肯定也為true。3. 傳遞性:對於任何三個非Null 物件x、y、z,如果x.equals(y)為true,並且y.equals(z),那麼x.equals(z)肯定也為true。4. 一致性:如果對象x和對象y沒有發生任何變換的話,反覆調用y.equals(x)應該返回的是一樣的結果。5. 對於任意非Null 物件x,x.equals(null)應該返回的是false。
針對這些特性,我們拿一個特性來解釋為什麼使用instanceof關鍵字來進行判斷有很大的問題。
假設,記住這裡是假設:如果Animal類的equals方法和Dog類的equals方法使用的是instanceof關鍵字來判斷的,也就是下面的代碼:
Animal類的equals方法:
@Override public boolean equals(Object obj) { if(!(obj instanceof Animal)) { return false; } Animal animal = (Animal) obj; return Objects.equals(this.name, animal.name); }
Dog類的equals方法:
@Override public boolean equals(Object obj) { if (!(obj instanceof Dog)) { return false; } Dog dog = (Dog) obj; return this.age == dog.age; }
然後我們在main方法裡面這麼寫:
public static void main(String[] args) { Animal a1 = new Animal("pby"); Dog d1 = new Dog("pby", 21); System.out.println(a1.equals(d1)); System.out.println(d1.equals(a1)); }
我們可以看到的是第一個結果返回的true,但是第二個返回的false。這個就有問題了,不符合equals方法的對稱性。
我們來分析一下,當a1.equals(d1)時,調用的Animal類中的equals方法,這個方法對name進行判斷,首先d1 instanceof Animal 肯定為true,因為Dog類是Animal的子類,所以if條件沒有屏蔽掉d1,由於兩個對象的name是相同的,所以傳回值是true,但是真正的結果是false,因為他們不屬於同一個類!至於第二個為什麼是false,這裡將不在解釋了!
然後我們回來看正確寫法,this.getClass == obj.getClass這個判斷就能夠將我們的d1屏蔽掉!
Java基礎-equals方法