Java Language Pitfall - Overloading instead of overriding


Example

Consider the following example:

public final class Person {
    private final String firstName;
    private final String lastName;
   
    public Person(String firstName, String lastName) {
        this.firstName = (firstName == null) ? "" : firstName;
        this.lastName = (lastName == null) ? "" : lastName;
    }

    public boolean equals(String other) {
        if (!(other instanceof Person)) {
            return false;
        }
        Person p = (Person) other;
        return firstName.equals(p.firstName) &&
                lastName.equals(p.lastName);
    }

    public int hashcode() {
        return firstName.hashCode() + 31 * lastName.hashCode();
    }
}

This code is not going to behave as expected. The problem is that the equals and hashcode methods for Person do not override the standard methods defined by Object.

  • The equals method has the wrong signature. It should be declared as equals(Object) not equals(String).
  • The hashcode method has the wrong name. It should be hashCode() (note the capital C).

These mistakes mean that we have declared accidental overloads, and these won't be used if Person is used in a polymorphic context.

However, there is a simple way to deal with this (from Java 5 onwards). Use the @Override annotation whenever you intend your method to be an override:

Java SE 5
public final class Person {
    ...

    @Override
    public boolean equals(String other) {
        ....
    }

    @Override
    public hashcode() {
        ....
    }
}

When we add an @Override annotation to a method declaration, the compiler will check that the method does override (or implement) a method declared in a superclass or interface. So in the example above, the compiler will give us two compilation errors, which should be enough to alert us to the mistake.