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:
- Reflexive:
x.equals(x)must be true - Symmetric: If
x.equals(y), theny.equals(x) - Transitive: If
x.equals(y)andy.equals(z), thenx.equals(z) - Consistent: Multiple calls return same result (if no changes)
- 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 ==
| Feature | equals() | == |
|---|---|---|
| Type | Method | Operator |
| Default | Reference comparison | Reference comparison |
| Override | Can override | Cannot override |
| Purpose | Content comparison | Reference comparison |
| Null safe | Can handle | NullPointerException |
| Example | s1.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:
- equals() compares object content
- Default implementation compares references
- Override to compare by content
- Parameter must be Object
- Check null and type
- Always override hashCode() too
- Follow 5 rules of equals() contract
- Use == for reference comparison
- Use equals() for content comparison
- 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()?