[改善Java代碼]在equals中使用getClass進行類型判斷


建議47: 在equals中使用getClass進行類型判斷

本節我們繼續討論覆寫equals的問題。這次我們編寫一個員工Employee類繼承Person類,這很正常,員工也是人嘛,而且在JEE中JavaBean有繼承關系也很常見,代碼如下:

 1 public class Client {  
 2     public static void main(String[] args) {  
 3          Employee e1 = new Employee("張三",100);  
 4          Employee e2 = new Employee("張三",1001);  
 5          Person p1 = new Person("張三");  
 6          System.out.println(p1.equals(e1));  
 7          System.out.println(p1.equals(e2));  
 8          System.out.println(e1.equals(e2));  
 9     }  
10 }  
11 
12 class Person{  
13     private String name;  
14 
15     public Person(String _name){  
16        name = _name;  
17     }  
18 
19     @Override  
20     public boolean equals(Object obj) {  
21          if(obj instanceof Person){  
22            Person p = (Person) obj;  
23            return name.equalsIgnoreCase(p.getName().trim());  
24          }  
25          return false;  
26     }
27 
28     public String getName() {
29         return name;
30     }
31 
32     public void setName(String name) {
33         this.name = name;
34     }  
35 } 
36 
37 class Employee extends Person{  
38     private int id;  
39     /*id的getter/setter方法省略*/  
40     public Employee(String _name,int _id) {  
41          super(_name);  
42          id = _id;  
43     }  
44 
45     public int getId() {
46         return id;
47     }
48 
49     public void setId(int id) {
50         this.id = id;
51     }
52 
53     @Override  
54     public boolean equals(Object obj) {  
55          if(obj instanceof Employee){  
56            Employee e = (Employee) obj;  
57            return super.equals(obj)&& e.getId() == id;  
58          }  
59          return false;  
60     }  
61 } 

輸出結果:

true
true
false

很不給力嘛,p1竟然等於e1,也等於e2,為什么不是同一個類的兩個實例竟然也會相等呢?這很簡單,因為p1.equals(e1) 是調用父類Person的equals方法進行判斷的,它使用instanceof關鍵字檢查e1是否是Person的實例,由於兩者存在繼承關系,那結果當然是true了,相等也就沒有任何問題了,但是反過來就不成立了,e1或e2可不等於p1,這也是違反對稱性原則的一個典型案例。

更玄的是p1與e1、e2相等,但e1竟然與e2不相等,似乎一個簡單的等號傳遞都不能實現。這才是我們要分析的真正重點:e1.equals(e2)調用的是子類Employee的equals方法,不僅僅要判斷姓名相同,還要判斷工號是否相同,兩者工號是不同的,不相等也是自然的了。等式不傳遞是因為違反了equals的傳遞性原則,傳遞性原則是指對於實例對象x、y、z來說,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也應該返回true。

這種情況發生的關鍵是父類使用了instanceof關鍵字,它是用來判斷是否是一個類的實例對象的,這很容易讓子類“鑽空子”。想要解決也很簡單,使用getClass來代替instanceof進行類型判斷,Person類的equals方法修改后如下所示:

 1 public boolean equals(Object obj) {  
 2      if(obj!=null && obj.getClass() == this.getClass()){  
 3         Person p = (Person) obj;  
 4         if(p.getName()==null || name==null){  
 5             return false;  
 6         }else{  
 7             return name.equalsIgnoreCase(p.getName());  
 8         }  
 9      }  
10      return false;  
11 } 

當然,考慮到Employee也有可能被繼承,也需要把它的instanceof修改為getClass。總之,在覆寫equals時建議使用getClass進行類型判斷,而不要使用instanceof。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM