????Object類中的equals方是用來(lái)判斷一個(gè)對(duì)象等于另一個(gè)對(duì)象,至于這個(gè)等于的條件需要自己定義,比如說(shuō),String類的equals相等的條件就是字符串的內(nèi)容必須相同,equals方法返回的值才為true。所以在我們?cè)谧约憾x的類中,equals的重寫是常見(jiàn)的!這里主要展示equals的特性和equals的正確寫法,至于equals方法具體的含義這里不介紹!
1. 舉一個(gè)例子
????在這介紹其他的,我們先來(lái)看看正確的寫法
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);
//這個(gè)方法只在JDK7及其以后才有的
//這個(gè)方法能夠保證兩個(gè)name其中只有一個(gè)為null的話,返回的false
//如果兩個(gè)都為null的話,返回的是true
//如果兩個(gè)都不為null的話,具體看情況
return Objects.equals(this.name, animal.name);
}
}
????從上面的代碼中我們看到是,Animal類的equals方法判斷的相等條件是name是否為相同。說(shuō)實(shí)話,判斷的條件非常的簡(jiǎn)單,但是我們的代碼寫的非常復(fù)雜??赡苡腥藭?huì)這樣寫的:
public boolean equals(Object obj) {
if(! (obj instanceof Animal)) {
return false;
}
Animal animal = (Animal) obj;
return this.name.equals(animal.name);
}
????實(shí)際上,上面的代碼是有很大的問(wèn)題,至于有什么問(wèn)題,待會(huì)再說(shuō)!這里我們來(lái)解釋一下正確equals方法寫的代碼:
1. this == obj, 毫無(wú)疑問(wèn),如果兩個(gè)對(duì)象的內(nèi)存都是相同的話,那么肯定是同一個(gè)對(duì)象了。
2. this == null, 同樣的話,如果傳進(jìn)來(lái)的對(duì)象是null的話,肯定為false。
3. this.getClass == obj.getClass, 這個(gè)條件可能讓人有點(diǎn)疑惑。我來(lái)解釋一下,getClass是獲得當(dāng)前的對(duì)象的Class對(duì)象,至于什么是Class對(duì)象,這里不解釋,需要的記得是:同一類的所有對(duì)象的獲得的Class對(duì)象都是同一個(gè)Class對(duì)象,也就是說(shuō),如果這里的this與obj不屬于同一個(gè)類的,那么肯定為false。
????那么這種寫法有好處呢,等我把列出另一個(gè)類的代碼再說(shuō)吧!
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;
}
}
????我們發(fā)現(xiàn)Dog類繼承于Animal類,并且重寫了父類的equals方法。在Dog類中equals方法,我們先來(lái)判斷了這兩個(gè)對(duì)象是否是相等的,其次再比較了age是否相同的。其中先調(diào)用了父類的equals來(lái)判斷,再來(lái)判斷子類本身的條件,這種方法有幾個(gè)好處:
1. 符合繼承的特性,當(dāng)且僅當(dāng)他們通過(guò)父類equals才有可能判斷子類的條件,因?yàn)槿绻慌袛喔割惖膃quals的話,當(dāng)父類的equals返回的是false,但是通過(guò)調(diào)用子類的equals卻返回true。這個(gè)根本不符合面向?qū)ο蟮奶匦?,?shí)際點(diǎn),如果兩個(gè)兒子老爸都不相同的話,兒子怎么可能相同!
2. 避免了obj是Dog類的子類(雖然這里Dog類沒(méi)有子類)。因?yàn)檫@里,我們先來(lái)調(diào)用父類的equals,父類的equals方法中:this.getClass() == obj.getClass 會(huì)幫助我們判斷這兩個(gè)類是否是同一個(gè)類。按照我們以往的寫法,使用instanceof關(guān)鍵字來(lái)進(jìn)行判斷的是有很大的問(wèn)題的,如果this是Dog類的對(duì)象,但是obj的實(shí)際類型是Dog子類的對(duì)象,如果使用 obj instanceof Dog 這個(gè)返回的肯定為true,如果通過(guò)了我們這個(gè)判斷語(yǔ)句,最后來(lái)判斷我們的age。實(shí)際上,這兩個(gè)對(duì)象肯定不是同一個(gè)對(duì)象,所以最后用age來(lái)判斷是有問(wèn)題的。至于這個(gè)問(wèn)題的詳細(xì)解釋,待會(huì)還會(huì)提及!
2. equals方法的特性
????上面留了一些伏筆,這里將詳細(xì)的解釋,但是在解釋之前,我們先來(lái)看看equals方法的特性:
1. 自反性:對(duì)于任何的非空對(duì)象,x.equals(x)返回肯定為true。
2. 對(duì)稱性:對(duì)于任何兩個(gè)非空對(duì)象x、y,如果x.equals(y)返回的是true,那么y.equals(x)返回的是肯定也為true。
3. 傳遞性:對(duì)于任何三個(gè)非空對(duì)象x、y、z,如果x.equals(y)為true,并且y.equals(z),那么x.equals(z)肯定也為true。
4. 一致性:如果對(duì)象x和對(duì)象y沒(méi)有發(fā)生任何變換的話,反復(fù)調(diào)用y.equals(x)應(yīng)該返回的是一樣的結(jié)果。
5. 對(duì)于任意非空對(duì)象x,x.equals(null)應(yīng)該返回的是false。
????針對(duì)這些特性,我們拿一個(gè)特性來(lái)解釋為什么使用instanceof關(guān)鍵字來(lái)進(jìn)行判斷有很大的問(wèn)題。
????假設(shè),記住這里是假設(shè):如果Animal類的equals方法和Dog類的equals方法使用的是instanceof關(guān)鍵字來(lái)判斷的,也就是下面的代碼:
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;
}
然后我們?cè)趍ain方法里面這么寫:
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));
}
????我們可以看到的是第一個(gè)結(jié)果返回的true,但是第二個(gè)返回的false。這個(gè)就有問(wèn)題了,不符合equals方法的對(duì)稱性。
????我們來(lái)分析一下,當(dāng)a1.equals(d1)時(shí),調(diào)用的Animal類中的equals方法,這個(gè)方法對(duì)name進(jìn)行判斷,首先d1 instanceof Animal 肯定為true,因?yàn)镈og類是Animal的子類,所以if條件沒(méi)有屏蔽掉d1,由于兩個(gè)對(duì)象的name是相同的,所以返回值是true,但是真正的結(jié)果是false,因?yàn)樗麄儾粚儆谕粋€(gè)類!至于第二個(gè)為什么是false,這里將不在解釋了!
????然后我們回來(lái)看正確寫法,this.getClass == obj.getClass這個(gè)判斷就能夠?qū)⑽覀兊膁1屏蔽掉!