Ghi đè phương thức java bằng () - không hoạt động?


150

Tôi đã gặp phải một vấn đề thú vị (và rất bực bội) với equals()phương pháp ngày hôm nay, điều khiến cho thứ mà tôi nghĩ là một lớp được kiểm tra tốt bị sập và gây ra một lỗi khiến tôi mất nhiều thời gian để theo dõi.

Để hoàn thiện, tôi đã không sử dụng IDE hoặc trình gỡ lỗi - chỉ là trình soạn thảo văn bản lỗi thời và System.out. Thời gian rất hạn chế và đó là một dự án trường học.

Dù sao đi nữa -

Tôi đã phát triển một giỏ mua hàng cơ bản mà có thể chứa một ArrayListsố Bookđối tượng . Để thực hiện addBook(), removeBook()hasBook()phương pháp của giỏ hàng, tôi muốn kiểm tra xem Bookđã tồn tại trong Cart. Vì vậy, tôi đi -

public boolean equals(Book b) {
    ... // More code here - null checks
    if (b.getID() == this.getID()) return true;
    else return false;
}

Tất cả đều hoạt động tốt trong thử nghiệm. Tôi tạo 6 đối tượng và điền chúng với dữ liệu. Thực hiện nhiều thao tác thêm, xóa, có () trên Cartvà mọi thứ đều hoạt động tốt. Tôi đọc rằng bạn có thể có equals(TYPE var)hoặcequals(Object o) { (CAST) var } nhưng cho rằng vì nó đang hoạt động nên nó không quá quan trọng.

Sau đó, tôi chạy vào một vấn đề - Tôi cần thiết để tạo ra một Bookđối tượng với chỉ những IDở trong đó từ bên trong lớp Book. Không có dữ liệu khác sẽ được nhập vào nó. Về cơ bản như sau:

public boolean hasBook(int i) {
    Book b = new Book(i);
    return hasBook(b);
}

public boolean hasBook(Book b) {
    // .. more code here
    return this.books.contains(b);
}

Đột nhiên, equals(Book b)phương pháp không còn hoạt động. Điều này đã mất một thời gian RẤT lâu để theo dõi mà không có trình sửa lỗi tốt và giả sử rằng Cartlớp đã được kiểm tra và sửa chữa đúng. Sau khi hoán đổi equals()phương thức như sau:

public boolean equals(Object o) {
    Book b = (Book) o;
    ... // The rest goes here   
}

Mọi thứ bắt đầu hoạt động trở lại. Có lý do nào để phương thức quyết định không lấy tham số Sách mặc dù rõ ràng nó một Bookđối tượng? Sự khác biệt duy nhất dường như là nó được khởi tạo từ trong cùng một lớp và chỉ chứa đầy một thành viên dữ liệu. Tôi rất rất bối rối. Xin vui lòng, làm sáng tỏ một số?


1
Tôi biết rằng tôi đã vi phạm 'Hợp đồng' liên quan đến việc ghi đè các phương thức bằng bằng cách phản chiếu - tuy nhiên tôi cần một cách nhanh chóng để kiểm tra xem đối tượng có tồn tại trong ArrayList mà không sử dụng tổng quát không.
Josh Smeaton

1
Đây là một bài học tốt để tìm hiểu về Java và bằng
jjnguy

Câu trả lời:


329

Trong Java, equals()phương thức được kế thừa từ Object:

public boolean equals(Object other);

Nói cách khác, tham số phải là loại Object. Điều này được gọi là ghi đè ; phương thức của bạn public boolean equals(Book other)thực hiện những gì được gọi là quá tải cho equals()phương thức.

Việc ArrayListsử dụng equals()các phương thức được ghi đè để so sánh nội dung (ví dụ: nội dung contains()equals()phương thức), không bị quá tải. Trong hầu hết các mã của bạn, việc gọi một mã không ghi đè chính xác Objectbằng là tốt, nhưng không tương thích ArrayList.

Vì vậy, không ghi đè phương thức chính xác có thể gây ra vấn đề.

Tôi ghi đè bằng mọi lúc sau:

@Override
public boolean equals(Object other){
    if (other == null) return false;
    if (other == this) return true;
    if (!(other instanceof MyClass)) return false;
    MyClass otherMyClass = (MyClass)other;
    ...test other properties here...
}

Việc sử dụng @Overridechú thích có thể giúp một tấn với những sai lầm ngớ ngẩn.

Sử dụng nó bất cứ khi nào bạn nghĩ rằng bạn đang ghi đè phương thức của siêu lớp 'hoặc giao diện. Bằng cách đó, nếu bạn làm sai cách, bạn sẽ gặp lỗi biên dịch.


31
Đây là một lập luận tốt ủng hộ chú thích @Override ... nếu OP đã sử dụng @Override trình biên dịch của anh ta sẽ nói với anh ta rằng anh ta thực sự không ghi đè phương thức lớp cha mẹ ...
Cowan

1
Không bao giờ biết về @Override, cảm ơn vì điều đó! Tôi cũng muốn thêm rằng ghi đè hashCode () thực sự nên được thực hiện và có thể đã phát hiện ra lỗi sớm hơn.
Josh Smeaton

5
Một số IDE (ví dụ Eclipse) thậm chí có thể tự động tạo các phương thức bằng () và hashcode () cho bạn dựa trên các biến thành viên của lớp.
sk.

1
if (!(other instanceof MyClass))return false;trả về falsenếu MyClassmở rộng lớp khác. Nhưng nó sẽ không trở lại falsenếu lớp khác mở rộng MyClass. Không nên equalít mâu thuẫn?
Robert

19
Khi sử dụng instanceof, nullcheck trước đó là dự phòng.
Mateusz Dymchot

108

Nếu bạn sử dụng nhật thực, chỉ cần vào menu trên cùng

Nguồn -> Tạo bằng () và hashCode ()


Tôi đồng ý! Điều này tôi chưa bao giờ biết trước đây và tạo ra nó làm cho nó ít bị lỗi hơn
Cậu bé

Tương tự ở đây. Cảm ơn Fred!
Anila

16
Trong IntelliJ, bạn tìm thấy điều này trong Mã → Tạo ra điều khiển hoặc điều khiển + N. :)
đúng vào

Trong Netbeans, bạn đi tới thanh menu> Nguồn (hoặc nhấp chuột phải)> Chèn mã (hoặc Ctrl-I) và nhấp vào Tạo bằng () ...
Solomon

11

Hơi lạc đề với câu hỏi của bạn, nhưng dù sao thì nó cũng có thể được đề cập đến:

Commons Lang đã có một số phương pháp tuyệt vời mà bạn có thể sử dụng để ghi đè bằng và mã băm. Hãy xem EqualsBuilder.reflectionEquals (...)HashCodeBuilder.reflectionHashCode (...) . Đã cứu tôi rất nhiều đau đầu trong quá khứ - mặc dù tất nhiên nếu bạn chỉ muốn làm "bằng" trên ID thì nó có thể không phù hợp với hoàn cảnh của bạn.

Tôi cũng đồng ý rằng bạn nên sử dụng @Overridechú thích bất cứ khi nào bạn ghi đè bằng (hoặc bất kỳ phương pháp nào khác).


4
Nếu bạn là người dùng nhật thực, bạn cũng có thể đi right click -> source -> generate hashCode() and equals(),
tunaranch

1
Tôi có đúng rằng các phương thức này được thực thi trong thời gian chạy không? Chúng ta sẽ không có vấn đề về hiệu suất trong trường hợp nếu chúng ta đi qua một bộ sưu tập lớn với các mục kiểm tra chúng xem có công bằng với một số mục khác vì sự phản chiếu không?
Gaket

4

Một giải pháp nhanh khác giúp tiết kiệm mã soạn sẵn là chú thích Lombok EqualsAndHashCode . Nó là dễ dàng, thanh lịch và tùy biến. Và không phụ thuộc vào IDE . Ví dụ;

import lombok.EqualsAndHashCode;

@EqualsAndHashCode(of={"errorNumber","messageCode"}) // Will only use this fields to generate equals.
public class ErrorMessage{

    private long        errorNumber;
    private int         numberOfParameters;
    private Level       loggingLevel;
    private String      messageCode;

Xem các tùy chọn có sẵn để tùy chỉnh các trường sẽ sử dụng trong bằng. Lombok có sẵn trong maven . Chỉ cần thêm nó với phạm vi được cung cấp :

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.14.8</version>
    <scope>provided</scope>
</dependency>

1

trong Android Studio là alt + insert ---> bằng và hashCode

Thí dụ:

    @Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Proveedor proveedor = (Proveedor) o;

    return getId() == proveedor.getId();

}

@Override
public int hashCode() {
    return getId();
}

1

Xem xét:

Object obj = new Book();
obj.equals("hi");
// Oh noes! What happens now? Can't call it with a String that isn't a Book...

1
@Elazar Làm sao vậy? objđược tuyên bố là một Object. Điểm thừa kế là sau đó bạn có thể gán a Bookcho obj. Sau đó, trừ khi bạn đề xuất rằng Objectkhông nên so sánh với Stringthông qua equals(), mã này phải hoàn toàn hợp pháp và trả lại false.
bcsb1001

Tôi đề nghị chính xác điều đó. Tôi tin rằng nó được chấp nhận rộng rãi.
Elazar

0

các instanceOftuyên bố thường được sử dụng trong việc thực hiện bình đẳng.

Đây là một cạm bẫy phổ biến!

Vấn đề là việc sử dụng instanceOfvi phạm quy tắc đối xứng:

(object1.equals(object2) == true) nếu và chỉ nếu (object2.equals(object1))

nếu bằng thứ nhất là đúng và object2 là một thể hiện của một lớp con của lớp mà obj1 thuộc về, thì bằng thứ hai sẽ trả về false!

nếu lớp được coi là nơi ob1 thuộc về được khai báo là cuối cùng, thì vấn đề này không thể phát sinh, nhưng nói chung, bạn nên kiểm tra như sau:

this.getClass() != otherObject.getClass(); nếu không, trả về false, nếu không thì kiểm tra các trường để so sánh cho bằng nhau!


3
Xem Bloch, Java hiệu quả, Mục 8, một phần lớn thảo luận về các vấn đề với việc ghi đè equals()phương thức. Ông đề nghị không sử dụng getClass(). Lý do chính là vì làm như vậy phá vỡ Nguyên tắc thay thế Liskov cho các lớp con không ảnh hưởng đến sự bình đẳng.
Stuart Marks

-1

recordId là tài sản của đối tượng

@Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Nai_record other = (Nai_record) obj;
        if (recordId == null) {
            if (other.recordId != null)
                return false;
        } else if (!recordId.equals(other.recordId))
            return false;
        return true;
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.