Equals Method

Introduction

equals() method compares two objects for equality. By default, it compares references.


Default Implementation

class Student {
    String name;
    int rollNo;

    Student(String name, int rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("John", 101);
        Student s2 = new Student("John", 101);
        Student s3 = s1;

        // Default equals() compares references
        System.out.println(s1.equals(s2));  // false (different objects)
        System.out.println(s1.equals(s3));  // true (same reference)
        System.out.println(s1 == s2);       // false
        System.out.println(s1 == s3);       // true
    }
}

Overriding equals()

Compare objects by content, not reference.

class Student {
    String name;
    int rollNo;

    Student(String name, int rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }

    @Override
    public boolean equals(Object obj) {
        // 1. Check if same reference
        if (this == obj) {
            return true;
        }

        // 2. Check if null
        if (obj == null) {
            return false;
        }

        // 3. Check if same class
        if (getClass() != obj.getClass()) {
            return false;
        }

        // 4. Cast and compare fields
        Student other = (Student) obj;
        return rollNo == other.rollNo && name.equals(other.name);
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("John", 101);
        Student s2 = new Student("John", 101);

        System.out.println(s1.equals(s2));  // true (same content)
        System.out.println(s1 == s2);       // false (different objects)
    }
}

equals() Contract

5 Rules:

  1. Reflexive: x.equals(x) must be true
  2. Symmetric: If x.equals(y), then y.equals(x)
  3. Transitive: If x.equals(y) and y.equals(z), then x.equals(z)
  4. Consistent: Multiple calls return same result (if no changes)
  5. Null: x.equals(null) must be false

Step-by-Step Implementation

class Person {
    private String name;
    private int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        // Step 1: Check if same object
        if (this == obj) {
            return true;
        }

        // Step 2: Check if null
        if (obj == null) {
            return false;
        }

        // Step 3: Check if same class
        if (getClass() != obj.getClass()) {
            return false;
        }

        // Step 4: Type cast
        Person person = (Person) obj;

        // Step 5: Compare fields
        if (age != person.age) {
            return false;
        }

        if (name == null) {
            return person.name == null;
        }

        return name.equals(person.name);
    }
}

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("John", 25);
        Person p2 = new Person("John", 25);
        Person p3 = new Person("Alice", 30);

        System.out.println(p1.equals(p2));  // true
        System.out.println(p1.equals(p3));  // false
        System.out.println(p1.equals(null)); // false
    }
}

equals() with Inheritance

class Employee {
    protected String name;
    protected double salary;

    Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

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

        Employee emp = (Employee) obj;
        return Double.compare(emp.salary, salary) == 0 && name.equals(emp.name);
    }
}

class Manager extends Employee {
    private String department;

    Manager(String name, double salary, String department) {
        super(name, salary);
        this.department = department;
    }

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

        // Call super.equals()
        if (!super.equals(obj)) return false;

        Manager manager = (Manager) obj;
        return department.equals(manager.department);
    }
}

public class Main {
    public static void main(String[] args) {
        Manager m1 = new Manager("John", 50000, "IT");
        Manager m2 = new Manager("John", 50000, "IT");
        Manager m3 = new Manager("John", 50000, "HR");

        System.out.println(m1.equals(m2));  // true
        System.out.println(m1.equals(m3));  // false (different dept)
    }
}

equals() and hashCode()

Always override both together!

class Student {
    String name;
    int rollNo;

    Student(String name, int rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }

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

        Student student = (Student) obj;
        return rollNo == student.rollNo && name.equals(student.name);
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + rollNo;
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("John", 101);
        Student s2 = new Student("John", 101);

        // Both must be consistent
        System.out.println(s1.equals(s2));        // true
        System.out.println(s1.hashCode() == s2.hashCode());  // true
    }
}

Common Mistakes

Mistake 1: Wrong Parameter Type

// ✗ Wrong - doesn't override
public boolean equals(Student obj) {
    return this.rollNo == obj.rollNo;
}

// ✓ Correct - overrides Object.equals()
@Override
public boolean equals(Object obj) {
    if (obj instanceof Student) {
        Student s = (Student) obj;
        return this.rollNo == s.rollNo;
    }
    return false;
}

Mistake 2: Not Checking null

// ✗ Wrong - NullPointerException
public boolean equals(Object obj) {
    Student s = (Student) obj;  // Crash if obj is null
    return this.rollNo == s.rollNo;
}

// ✓ Correct
public boolean equals(Object obj) {
    if (obj == null) return false;
    // Then cast
}

Mistake 3: Not Checking Type

// ✗ Wrong - ClassCastException
public boolean equals(Object obj) {
    Student s = (Student) obj;  // Crash if different type
    return this.rollNo == s.rollNo;
}

// ✓ Correct
public boolean equals(Object obj) {
    if (getClass() != obj.getClass()) return false;
    // Then cast
}

instanceof vs getClass()

Using instanceof:

@Override
public boolean equals(Object obj) {
    if (obj instanceof Student) {
        Student s = (Student) obj;
        return this.rollNo == s.rollNo;
    }
    return false;
}
// Allows subclass comparison

Using getClass():

@Override
public boolean equals(Object obj) {
    if (obj == null || getClass() != obj.getClass()) {
        return false;
    }
    Student s = (Student) obj;
    return this.rollNo == s.rollNo;
}
// Exact class match only

Complete Example

class BankAccount {
    private String accountNumber;
    private String holderName;
    private double balance;

    BankAccount(String accountNumber, String holderName, double balance) {
        this.accountNumber = accountNumber;
        this.holderName = holderName;
        this.balance = balance;
    }

    @Override
    public boolean equals(Object obj) {
        // Same reference
        if (this == obj) {
            return true;
        }

        // Null check
        if (obj == null) {
            return false;
        }

        // Type check
        if (getClass() != obj.getClass()) {
            return false;
        }

        // Cast to BankAccount
        BankAccount account = (BankAccount) obj;

        // Compare account number only (unique identifier)
        return accountNumber.equals(account.accountNumber);
    }

    @Override
    public int hashCode() {
        return accountNumber.hashCode();
    }

    @Override
    public String toString() {
        return "Account[" + accountNumber + ", " + holderName + ", $" + balance + "]";
    }
}

public class Main {
    public static void main(String[] args) {
        BankAccount acc1 = new BankAccount("A001", "John", 1000);
        BankAccount acc2 = new BankAccount("A001", "John Doe", 2000);
        BankAccount acc3 = new BankAccount("A002", "Alice", 1500);

        System.out.println("acc1: " + acc1);
        System.out.println("acc2: " + acc2);
        System.out.println("acc3: " + acc3);

        System.out.println();

        // Same account number = equal
        System.out.println("acc1.equals(acc2): " + acc1.equals(acc2));  // true
        System.out.println("acc1.equals(acc3): " + acc1.equals(acc3));  // false
        System.out.println("acc1 == acc2: " + (acc1 == acc2));          // false
    }
}

Real-World Example: Product

class Product {
    private String productId;
    private String name;
    private double price;

    Product(String productId, String name, double price) {
        this.productId = productId;
        this.name = name;
        this.price = price;
    }

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

        Product product = (Product) obj;

        // Products equal if ID is same
        return productId.equals(product.productId);
    }

    @Override
    public int hashCode() {
        return productId.hashCode();
    }
}

public class Main {
    public static void main(String[] args) {
        Product p1 = new Product("P001", "Laptop", 50000);
        Product p2 = new Product("P001", "Laptop Updated", 55000);

        // Same product ID = equal products
        System.out.println(p1.equals(p2));  // true
    }
}

Testing equals() Method

class Student {
    String name;
    int rollNo;

    Student(String name, int rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }

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

        Student student = (Student) obj;
        return rollNo == student.rollNo && name.equals(student.name);
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("John", 101);
        Student s2 = new Student("John", 101);
        Student s3 = new Student("Alice", 102);

        // Test reflexive: x.equals(x)
        System.out.println("Reflexive: " + s1.equals(s1));  // true

        // Test symmetric: x.equals(y) = y.equals(x)
        System.out.println("Symmetric: " + (s1.equals(s2) == s2.equals(s1)));  // true

        // Test null
        System.out.println("Null: " + s1.equals(null));  // false

        // Test different objects
        System.out.println("Different: " + s1.equals(s3));  // false

        // Test same content
        System.out.println("Same content: " + s1.equals(s2));  // true
    }
}

equals() vs ==

Featureequals()==
TypeMethodOperator
DefaultReference comparisonReference comparison
OverrideCan overrideCannot override
PurposeContent comparisonReference comparison
Null safeCan handleNullPointerException
Examples1.equals(s2)s1 == s2

String equals()

public class Main {
    public static void main(String[] args) {
        String s1 = "Hello";
        String s2 = "Hello";
        String s3 = new String("Hello");

        System.out.println(s1 == s2);         // true (string pool)
        System.out.println(s1 == s3);         // false (different objects)
        System.out.println(s1.equals(s3));    // true (same content)
    }
}

Template for equals()

@Override
public boolean equals(Object obj) {
    // 1. Same reference?
    if (this == obj) return true;

    // 2. Null?
    if (obj == null) return false;

    // 3. Same class?
    if (getClass() != obj.getClass()) return false;

    // 4. Cast
    ClassName other = (ClassName) obj;

    // 5. Compare fields
    // For primitives: field1 == other.field1
    // For objects: field1.equals(other.field1)
    // Handle null: if (field1 == null) return other.field1 == null;

    return true;  // or comparison result
}

Quick Reference

// Default behavior
Student s1 = new Student("John", 101);
Student s2 = new Student("John", 101);
s1.equals(s2);  // false (compares references)

// Override for content comparison
@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null || getClass() != obj.getClass()) return false;

    Student s = (Student) obj;
    return rollNo == s.rollNo && name.equals(s.name);
}

// Now
s1.equals(s2);  // true (compares content)

// Remember
- Always override with Object parameter
- Check null and type
- Override hashCode() too
- Follow equals() contract

Exam Tips

Remember:

  1. equals() compares object content
  2. Default implementation compares references
  3. Override to compare by content
  4. Parameter must be Object
  5. Check null and type
  6. Always override hashCode() too
  7. Follow 5 rules of equals() contract
  8. Use == for reference comparison
  9. Use equals() for content comparison
  10. String overrides equals() (compares text)

Common Questions:

  • What is equals() method?
  • Default behavior of equals()?
  • How to override equals()?
  • equals() vs ==?
  • What is equals() contract?
  • Why override hashCode() with equals()?
  • How to compare null in equals()?
  • instanceof vs getClass() in equals()?