Skip to content

Beginner · Classes and objects · topic 20 of 60 · 30 min

toString, equals, hashCode and records

Explanation

Every class inherits toString, equals and hashCode from Object. Override toString for readable output, and override equals and hashCode TOGETHER so that equal objects have equal hash codes, or HashSet and HashMap will misbehave. Records (Java 16+) generate all three, plus the constructor and accessors, for simple immutable data classes.

What every object inherits

Every Java class extends java.lang.Object, directly or indirectly, and inherits methods including toString(), equals(Object) and hashCode(). The default versions are rarely what you want: toString prints something like Product@1b6d3586, and equals behaves like == (same object only).

toString

java
public class Product {
    private final String sku;
    private final double price;
    public Product(String sku, double price) { this.sku = sku; this.price = price; }

    @Override
    public String toString() {
        return "Product[sku=" + sku + ", price=" + price + "]";
    }
}
System.out.println(new Product("PEN-01", 20));   // Product[sku=PEN-01, price=20.0]
@Override asks the compiler to check that you really are overriding a parent method (a typo becomes a compile error).

equals and hashCode: a contract

equals decides whether two objects are "the same value" (two Product objects with the same SKU, for example). hashCode returns an int used by hash-based collections (HashMap, HashSet) to decide which bucket an object goes into. The contract: if a.equals(b) is true, then a.hashCode() must equal b.hashCode(). If you override equals but not hashCode, two equal objects land in different buckets and a HashSet will happily store both "duplicates".

java
import java.util.Objects;

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Product other)) return false;   // pattern matching (Java 16+)
    return sku.equals(other.sku);
}

@Override
public int hashCode() {
    return Objects.hash(sku);   // same fields as equals
}
Careful: Overriding equals without hashCode is a classic bug that interviewers love: set.add(new Product("PEN-01", 20)); set.contains(new Product("PEN-01", 20)) returns false. Always override both, using the same fields. Also, never change a field used in hashCode while the object is inside a HashSet or used as a HashMap key.

Records: data classes in one line

Many classes just carry data: a DTO from an API, a point, a money amount. Writing fields, a constructor, getters, equals, hashCode and toString by hand is long and error-prone. A record (Java 16+) declares the components once, and the compiler generates the rest. Records are immutable: their fields are private and final, and there are no setters.

java
public record Product(String sku, double price) { }

Product p = new Product("PEN-01", 20);
System.out.println(p.sku());           // accessor is sku(), not getSku()
System.out.println(p);                 // Product[sku=PEN-01, price=20.0]
System.out.println(p.equals(new Product("PEN-01", 20)));   // true (all components compared)

Compact constructors for validation

java
public record Money(long paise, String currency) {
    public Money {                      // compact constructor: no parameter list
        if (paise < 0) throw new IllegalArgumentException("Negative amount");
        currency = currency.toUpperCase();   // you may normalise parameters before they are stored
    }
    public Money plus(Money other) { return new Money(paise + other.paise, currency); }
}

Records can have methods and static members, and can implement interfaces, but cannot extend another class and cannot declare extra instance fields. Use a normal class when the object needs mutable state or inheritance.

Think of it like this: equals and hashCode are like a library's rules for books. equals says two copies with the same ISBN are "the same book". hashCode is the shelf number. If two copies of the same ISBN were shelved in different places, the librarian checking one shelf would wrongly say "we don't have it".
Tip: Let your IDE generate equals, hashCode and toString for ordinary classes, or use a record. Hand-written versions often forget a field.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

toString, equals, hashCode and records · 1/6

Object methods and records

In this lesson you will give classes proper toString, equals and hashCode, and meet records.

Prefer a video by a teacher? Find free videos on “java equals hashcode contract records tutorial” on YouTube ↗

In real life: De-duplicating customers from two systems

After a bank merger, customer lists from two systems were combined into a HashSet to remove duplicates, keyed by PAN. Because the Customer class overrode equals but forgot hashCode, duplicates survived and some customers received two welcome letters. Turning Customer into a record keyed on PAN fixed it.

java
// A record compares ALL its components, so the de-duplication key holds only the PAN.
record CustomerKey(String pan) { }
var seen = new java.util.HashSet<CustomerKey>();
System.out.println(seen.add(new CustomerKey("ABCDE1234F")));   // true
System.out.println(seen.add(new CustomerKey("ABCDE1234F")));   // false: duplicate

Practice (0/3 done)

Task 1 · Question

class P { String id; P(String id) { this.id = id; } public boolean equals(Object o) { return o instanceof P p && p.id.equals(id); } } — no hashCode. What does this probably print? var set = new HashSet<P>(); set.add(new P("A")); System.out.println(set.contains(new P("A")));

Task 2 · Question

record Point(int x, int y) {} Which line does NOT compile?

Task 3 · Practice

Write a class Book with isbn, title and price. Override toString, equals and hashCode so that two books are equal when their ISBN is equal. Prove it by adding two Book objects with the same ISBN to a HashSet and printing its size (it must be 1). Then rewrite Book as a record and explain in a comment why the record version compares all three fields.

Quick quiz

Question 1

1.If a.equals(b) is true, what must be true?

Question 2

2.What does @Override do?

Question 3

3.Which is TRUE about records?

Question 4

4.Default Object.equals behaves like…

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

If a.equals(b) is true, what must be true?

Answer: a.hashCode() == b.hashCode()

That is the equals/hashCode contract.

What does @Override do?

Answer: Asks the compiler to check that the method really overrides a parent method

A typo in the signature becomes a compile error.

Which is TRUE about records?

Answer: They generate equals, hashCode and toString

Records are immutable data carriers with generated methods.

Default Object.equals behaves like…

Answer: ==, same object only

Without overriding, equals checks identity.

Without overriding ToString, what does printing an object of class City show?Related

Answer: Its class name (like City)

The default ToString returns the type's full name.

From Classes, objects and constructors

What does == compare for two data class objects?Related

Answer: Their property values

Data classes generate equals based on properties.

From Data, enum and sealed classes, and objects

What does new Product("Pen", 10m).ToString() give for record Product(string Name, decimal Price)?Related

Answer: Product { Name = Pen, Price = 10 }

Records print their type name and properties.

From Records and pattern matching

How many classes can a Java class directly extend?Related

Answer: One

Java has single inheritance for classes.

From Inheritance with extends

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. This is the last topic of “Classes and objects”. The module exam comes next.
  2. Module 4 exam: Classes and objects · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Inheritance, polymorphism and interfaces. 60 topics left in the course before the final exam.

← Previous: static members and final