2013-09-02 30 views
13

Đó là tuyên bố trong .equals(Object) javadoc Object của:Java .equals() instanceof subclass? Tại sao không gọi superclass bằng thay vì làm cho nó cuối cùng?

Nó là đối xứng: đối với bất kỳ tài liệu tham khảo không null giá trị x và y, x.equals (y) sẽ trả về đúng nếu và chỉ nếu y.equals (x) trả về đúng.

Hầu như ở khắp mọi nơi trong ví dụ mã tôi thấy ghi đè .equals(Object) phương pháp trong đó sử dụng instanceof là một trong những thử nghiệm đầu tiên, ví dụ ở đây: What issues/pitfalls must be considered when overriding equals and hashCode?

public class Person { 
    private String name; 
    private int age; 

    public boolean equals(Object obj) { 
     if (obj == null) 
      return false; 
     if (obj == this) 
      return true; 
     if (!(obj instanceof Person)) 
      return false; 
     ... 
    } 

}

Bây giờ với class SpecialPerson extends Person có trong equals:

 if (!(obj instanceof SpecialPerson)) 
      return false; 

chúng tôi không đảm bảo rằng .equals() là đối xứng. Nó đã được thảo luận ví dụ ở đây: any-reason-to-prefer-getclass-over-instanceof-when-generating-equals

Person a = new Person(), b = new SpecialPerson(); 

a.equals(b); //sometimes true, since b instanceof Person 
b.equals(a); //always false 

Có lẽ tôi nên thêm vào đầu SpecialPerson của bằng cuộc gọi trực tiếp đến siêu?

public boolean equals(Object obj) { 
     if(!obj instanceof SpecialPerson) 
      return super.equals(obj); 
     ... 
     /* more equality tests here */ 
    } 
+0

Vấn đề tương tự cũng được thảo luận trong java hiệu quả. Bạn có thể đọc nó. – Mangoose

+2

Để thêm vào nhận xét của Manoose - Sais Java hiệu quả: "Cách dễ nhất để tránh các vấn đề là không ghi đè phương thức bằng , trong trường hợp đó mỗi cá thể chỉ bằng với chính nó. Đây là điều phải làm nếu có bất kỳ nào của áp dụng các điều kiện sau: [...] Một lớp cha đã ghi đè bằng, và hành vi được kế thừa từ lớp cha là thích hợp cho lớp này. " – Fildor

+0

bản sao có thể có của [Ghi đè bằng và mã băm trong Java] (http://stackoverflow.com/questions/27581/overriding-equals-and-hashcode-in-java) –

Trả lời

9

Rất nhiều ví dụ sử dụng instanceof vì hai lý do: a) nó gấp kiểm tra và kiểm tra loại trống thành một hoặc b) ví dụ dành cho Hibernate hoặc một số khung viết mã khác.

Giải pháp "đúng" (theo JavaDoc) là sử dụng this.getClass() == obj.getClass(). Điều này làm việc cho Java vì các lớp là các trình đơn và VM đảm bảo điều này. Nếu bạn hoang tưởng, bạn có thể sử dụng this.getClass().equals(obj.getClass()) nhưng hai thực sự là tương đương.

Điều này hoạt động phần lớn thời gian. Nhưng đôi khi, các khung công tác Java cần phải làm những điều "thông minh" với mã byte. Điều này thường có nghĩa là họ tạo một loại phụ tự động. Vì loại phụ nên được xem xét tương đương với kiểu gốc, equals() phải được triển khai theo cách "sai" nhưng điều này không quan trọng vì khi chạy, các kiểu con sẽ tuân theo một số mẫu nhất định. Ví dụ, họ sẽ làm công cụ bổ sung trước khi một setter đang được gọi. Điều này không ảnh hưởng đến "sự bình đẳng".

Khi bạn nhận thấy, mọi thứ bắt đầu trở nên xấu xí khi bạn có cả hai trường hợp: Bạn thực sự mở rộng các loại cơ sở và bạn kết hợp với thế hệ kiểu phụ tự động. Nếu bạn làm điều đó, bạn phải chắc chắn rằng bạn không bao giờ sử dụng các loại không phải lá.

+0

Bạn có thể trích dẫn phần tài liệu phù hợp với đoạn thứ hai của mình không? – arshajii

+0

@arshajii: Đó là một phần của hợp đồng bộ nạp lớp. Trình nạp lớp chuẩn sẽ luôn trả lại cùng một tham chiếu cho cùng một tên lớp. Cược tắt cho các trình nạp lớp kịch bản, tất nhiên. –

+0

Ồ, ý tôi là phần "Giải pháp đúng là sử dụng ...".Bạn có nói rằng tốt hơn để so sánh 'getClass()' s trong 'equals()' thay vì sử dụng 'instanceof'? – arshajii

0

Nỗ lực giải quyết vấn đề của bạn không chính xác. Giả sử bạn có 2 lớp con SpecialPersonBizarrePerson. Với việc triển khai này, BizarrePerson trường hợp có thể bằng SpecialPerson trường hợp. Bạn thường không muốn điều đó.

+2

Cũng có nhiều trường hợp có ý nghĩa khi có các phiên bản của hai phân lớp con khác nhau được coi là bằng nhau. Lấy ví dụ 'List'. Một 'ArrayList' là và nên bằng với một' LinkedList' chứa các phần tử giống nhau. – arshajii

+1

Nhưng sau đó bạn có thể không muốn ghi đè bằng trong lớp con, đó là những gì câu hỏi này là tất cả về. –

1

Bạn đang thiếu thứ gì đó ở đây. Tôi sẽ cố gắng làm nổi bật điều này:

Giả sử bạn có Person person = new Person()Person personSpecial = new SpecialPerson() thì tôi chắc chắn bạn sẽ không thích hai đối tượng này bằng nhau. Vì vậy, nó thực sự làm việc theo yêu cầu, bằng nhau phải trả về false.

Hơn nữa, đối xứng quy định rằng phương pháp equals() trong cả hai lớp phải tuân theo cùng một lúc. Nếu một bằng trả về true và trả về false, thì tôi sẽ nói lỗ hổng là bằng giá trị bằng.

0

không sử dụng instanceof. sử dụng this.getClass() == obj.getClass() để thay thế. thì bạn đang kiểm tra lớp chính xác này.

khi làm việc với equals bạn luôn phải sử dụng số hashCode và ghi đè quá!

phương thức hashCode cho Người có thể nhìn như thế này:

@Override 
public int hashCode() 
{ 
    final int prime = 31; 
    int result = 1; 
    result = prime * result + age; 
    result = prime * result + ((name == null) ? 0 : name.hashCode()); 
    return result; 
} 

và sử dụng nó như thế này trong bằng phương pháp của bạn:

if (this.hashCode() != obj.hashCode()) 
{ 
    return false; 
} 
0

Một loại không nên xem xét bản thân bằng một đối tượng của bất kỳ loại khác - ngay cả một loại phụ - trừ khi cả hai đối tượng đều xuất phát từ một lớp chung có hợp đồng chỉ định cách con cháu của các loại khác nhau nên kiểm tra tính bình đẳng. Ví dụ, một lớp trừu tượng StringyThing có thể đóng gói các chuỗi và cung cấp các phương thức để thực hiện những việc như chuyển đổi thành chuỗi hoặc trích xuất các đoạn mã, nhưng không áp đặt bất kỳ yêu cầu nào về định dạng sao lưu. Ví dụ, một loại phụ có thể là StringyThing có thể chứa một mảng StringyThing và đóng gói giá trị của việc ghép nối tất cả các chuỗi đó. Hai trường hợp của StringyThing sẽ được xác định là bằng nhau nếu chuyển đổi thành chuỗi sẽ mang lại kết quả giống nhau và so sánh giữa hai trường hợp khác nhau không thể phân biệt được StringyThing mà các loại không biết gì về nhau có thể phải trả về điều đó, nhưng StringyThing loại được nhập có thể bao gồm mã để tối ưu hóa các trường hợp khác nhau. Ví dụ, nếu một StringyThing đại diện cho "M lặp lại của nhân vật ch" và một đại diện cho "N lặp đi lặp lại của chuỗi St", và loại thứ hai biết về lần đầu tiên, nó có thể kiểm tra xem St chứa gì, nhưng M/N lặp đi lặp lại của nhân vật ch. Việc kiểm tra như vậy sẽ cho biết các chuỗi có bằng nhau hay không, mà không cần phải "mở rộng" một trong hai chuỗi đó.

Các vấn đề liên quan