zaro

Does equals return false in Java?

Published in Java Equals Method 5 mins read

Yes, the equals method in Java can indeed return false. This is its intended behavior when two objects are determined not to be equal.

Understanding the equals Method in Java

The equals method in Java is fundamental for object comparison. Defined in the java.lang.Object class, its primary purpose is to determine if two objects are "equal" based on a defined criterion.

Default equals Behavior

By default, the equals method in java.lang.Object performs a reference comparison. This means it returns true only if both object references point to the exact same memory location (i.e., this == obj). Consequently, for two distinct objects, even if they contain identical data, the default equals method will return false.

For instance:

Object obj1 = new Object();
Object obj2 = new Object();
System.out.println(obj1.equals(obj2)); // Returns false
System.out.println(obj1.equals(obj1)); // Returns true

Overriding equals for Value Equality

In many cases, you want to compare objects based on their state or values rather than their memory location. This is why classes often override the equals method to provide "value equality." For example, the String class overrides equals to compare the actual sequence of characters.

When overridden, equals will return false if the values or states of the two objects being compared are different.

Example of equals returning false due to differing values:

String str1 = new String("hello");
String str2 = new String("world");
System.out.println(str1.equals(str2)); // Returns false

The Critical Case: When equals Incorrectly Returns false

While equals returning false is a normal and expected outcome when objects are not equivalent, there is a problematic scenario where equals is implemented to always return false. This implementation fundamentally breaks the equals contract defined by the java.lang.Object class.

The equals Contract and Reflexivity

The equals method has a strict contract that all custom implementations must adhere to. This contract ensures consistent and predictable behavior, especially when objects are used in collections like Maps or Sets. One of the core requirements is reflexivity:

  • Reflexivity: For any non-null reference value x, x.equals(x) must return true.

If a class defines an equals method that always returns false, it violates this crucial reflexivity requirement. This means that an object would not be considered equal to itself (x.equals(x) would yield false), which is a logical contradiction.

Implications of a Broken equals Method

An equals implementation that always returns false leads to severe issues:

  • Object Not Equal to Itself: The most direct consequence is that myObject.equals(myObject) would return false, which is counter-intuitive and incorrect.
  • Unusable Collections: It becomes impossible to create useful Maps or Sets of that class. These collections rely heavily on the equals method (and hashCode) to determine object uniqueness and retrieval. If equals always returns false, every object inserted would be considered unique, and attempts to retrieve or check for containment would likely fail, as no object would be found equal to the one being searched for, even if it's the exact same instance.
  • Violates API Requirements: It fundamentally disrespects the intended behavior and contract of the java.lang.Object.equals method.

Example of an Incorrect equals Implementation:

class MyBrokenObject {
    private int id;

    public MyBrokenObject(int id) {
        this.id = id;
    }

    @Override
    public boolean equals(Object obj) {
        // This implementation always returns false, violating the equals contract
        return false;
    }

    @Override
    public int hashCode() {
        return id; // Even if hashCode is correct, equals is broken
    }
}

If you use MyBrokenObject in a HashSet:

import java.util.HashSet;
import java.util.Set;

public class EqualsIssueDemo {
    public static void main(String[] args) {
        MyBrokenObject objA = new MyBrokenObject(1);
        MyBrokenObject objB = new MyBrokenObject(1); // Same 'id' value

        System.out.println("objA.equals(objA): " + objA.equals(objA)); // Expected true, but outputs false!
        System.out.println("objA.equals(objB): " + objA.equals(objB)); // Outputs false

        Set<MyBrokenObject> mySet = new HashSet<>();
        mySet.add(objA);
        mySet.add(objB); // objB has the same 'id' but hashCode and equals will not work as expected

        System.out.println("Set size: " + mySet.size()); // Likely 2, because equals always returns false
        System.out.println("Set contains objA: " + mySet.contains(objA)); // Likely false, as equals(objA, objA) is false
    }
}

The output objA.equals(objA): false clearly demonstrates the violation of reflexivity and the problems it introduces, including with collections.

Key Principles for a Proper equals Implementation

To ensure your equals method behaves correctly and is compatible with Java's ecosystem (especially collections), always adhere to its contract:

Property Description
Reflexive x.equals(x) must be true.
Symmetric If x.equals(y) is true, then y.equals(x) must be true.
Transitive If x.equals(y) is true and y.equals(z) is true, then x.equals(z) must be true.
Consistent Multiple invocations of x.equals(y) must return the same result, provided no information used in equals comparisons on the objects is modified.
Non-null For any non-null reference value x, x.equals(null) must return false.

Furthermore, it's crucial that if you override equals, you must also override hashCode. This ensures that objects considered equal by equals produce the same hash code, which is vital for the correct operation of hash-based collections like HashMap and HashSet.

In conclusion, while equals can and should return false when objects are not equivalent, an implementation that always returns false is fundamentally flawed and indicative of a broken class design.