[java] Overriding the java equals() method - not working?

I ran into an interesting (and very frustrating) issue with the equals() method today which caused what I thought to be a well tested class to crash and cause a bug that took me a very long time to track down.

Just for completeness, I wasn't using an IDE or debugger - just good old fashioned text editor and System.out's. Time was very limited and it was a school project.

Anyhow -

I was developing a basic shopping cart which could contain an ArrayList of Book objects. In order to implement the addBook(), removeBook(), and hasBook() methods of the Cart, I wanted to check if the Book already existed in the Cart. So off I go -

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

All works fine in testing. I create 6 objects and fill them with data. Do many adds, removes, has() operations on the Cart and everything works fine. I read that you can either have equals(TYPE var) or equals(Object o) { (CAST) var } but assumed that since it was working, it didn't matter too much.

Then I ran into a problem - I needed to create a Book object with only the ID in it from within the Book class. No other data would be entered into it. Basically the following:

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);
}

All of a sudden, the equals(Book b) method no longer works. This took a VERY long time to track down without a good debugger and assuming the Cart class was properly tested and correct. After swaapping the equals() method to the following:

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

Everything began to work again. Is there a reason the method decided not to take the Book parameter even though it clearly was a Book object? The only difference seemed to be it was instantiated from within the same class, and only filled with one data member. I'm very very confused. Please, shed some light?

This question is related to java equals overriding

The answer is


In Java, the equals() method that is inherited from Object is:

public boolean equals(Object other);

In other words, the parameter must be of type Object. This is called overriding; your method public boolean equals(Book other) does what is called overloading to the equals() method.

The ArrayList uses overridden equals() methods to compare contents (e.g. for its contains() and equals() methods), not overloaded ones. In most of your code, calling the one that didn't properly override Object's equals was fine, but not compatible with ArrayList.

So, not overriding the method correctly can cause problems.

I override equals the following everytime:

@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...
}

The use of the @Override annotation can help a ton with silly mistakes.

Use it whenever you think you are overriding a super class' or interface's method. That way, if you do it the wrong way, you will get a compile error.


If you use eclipse just go to the top menu

Source --> Generate equals() and hashCode()


Consider:

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

recordId is property of the object

@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;
    }

the instanceOf statement is often used in implementation of equals.

This is a popular pitfall !

The problem is that using instanceOf violates the rule of symmetry:

(object1.equals(object2) == true) if and only if (object2.equals(object1))

if the first equals is true, and object2 is an instance of a subclass of the class where obj1 belongs to, then the second equals will return false!

if the regarded class where ob1 belongs to is declared as final, then this problem can not arise, but in general, you should test as follows:

this.getClass() != otherObject.getClass(); if not, return false, otherwise test the fields to compare for equality!


in Android Studio is alt + insert ---> equals and hashCode

Example:

    @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();
}

Slightly off-topic to your question, but it's probably worth mentioning anyway:

Commons Lang has got some excellent methods you can use in overriding equals and hashcode. Check out EqualsBuilder.reflectionEquals(...) and HashCodeBuilder.reflectionHashCode(...). Saved me plenty of headache in the past - although of course if you just want to do "equals" on ID it may not fit your circumstances.

I also agree that you should use the @Override annotation whenever you're overriding equals (or any other method).


recordId is property of the object

@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;
    }

If you use eclipse just go to the top menu

Source --> Generate equals() and hashCode()


Consider:

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

Slightly off-topic to your question, but it's probably worth mentioning anyway:

Commons Lang has got some excellent methods you can use in overriding equals and hashcode. Check out EqualsBuilder.reflectionEquals(...) and HashCodeBuilder.reflectionHashCode(...). Saved me plenty of headache in the past - although of course if you just want to do "equals" on ID it may not fit your circumstances.

I also agree that you should use the @Override annotation whenever you're overriding equals (or any other method).


Another fast solution that saves boilerplate code is Lombok EqualsAndHashCode annotation. It is easy, elegant and customizable. And does not depends on the IDE. For example;

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;

See the options avaliable to customize which fields to use in the equals. Lombok is avalaible in maven. Just add it with provided scope:

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

Slightly off-topic to your question, but it's probably worth mentioning anyway:

Commons Lang has got some excellent methods you can use in overriding equals and hashcode. Check out EqualsBuilder.reflectionEquals(...) and HashCodeBuilder.reflectionHashCode(...). Saved me plenty of headache in the past - although of course if you just want to do "equals" on ID it may not fit your circumstances.

I also agree that you should use the @Override annotation whenever you're overriding equals (or any other method).


in Android Studio is alt + insert ---> equals and hashCode

Example:

    @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();
}

Slightly off-topic to your question, but it's probably worth mentioning anyway:

Commons Lang has got some excellent methods you can use in overriding equals and hashcode. Check out EqualsBuilder.reflectionEquals(...) and HashCodeBuilder.reflectionHashCode(...). Saved me plenty of headache in the past - although of course if you just want to do "equals" on ID it may not fit your circumstances.

I also agree that you should use the @Override annotation whenever you're overriding equals (or any other method).


Another fast solution that saves boilerplate code is Lombok EqualsAndHashCode annotation. It is easy, elegant and customizable. And does not depends on the IDE. For example;

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;

See the options avaliable to customize which fields to use in the equals. Lombok is avalaible in maven. Just add it with provided scope:

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

the instanceOf statement is often used in implementation of equals.

This is a popular pitfall !

The problem is that using instanceOf violates the rule of symmetry:

(object1.equals(object2) == true) if and only if (object2.equals(object1))

if the first equals is true, and object2 is an instance of a subclass of the class where obj1 belongs to, then the second equals will return false!

if the regarded class where ob1 belongs to is declared as final, then this problem can not arise, but in general, you should test as follows:

this.getClass() != otherObject.getClass(); if not, return false, otherwise test the fields to compare for equality!


Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to equals

this in equals method Why do we have to override the equals() method in Java? Compare two objects with .equals() and == operator Check if bash variable equals 0 Setting equal heights for div's with jQuery Java, how to compare Strings with String Arrays How can I express that two values are not equal to eachother? How to override equals method in Java How do you say not equal to in Ruby? Getting an element from a Set

Examples related to overriding

How to underline a UILabel in swift? How to 'update' or 'overwrite' a python list maven command line how to point to a specific settings.xml for a single command? How to override the properties of a CSS class using another CSS class What is the difference between dynamic and static polymorphism in Java? Overriding css style? Android Overriding onBackPressed() What is the 'override' keyword in C++ used for? Why do we have to override the equals() method in Java? Can overridden methods differ in return type?