Syntax
@Override\npublic boolean equals(Object o) { }\n@Override\npublic int hashCode() { }\n@Override\npublic String toString() { }Examples
The Problem with Default Behavior
Without overrides, two objects with identical data are not considered equal.
class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
}
Point p1 = new Point(3, 4);
Point p2 = new Point(3, 4);
System.out.println(p1 == p2); // false - different objects
System.out.println(p1.equals(p2)); // false - default equals() is the same as ==
System.out.println(p1); // Point@1b6d3586 - unreadable default toString()Overriding toString()
Providing a readable, meaningful string representation of an object.
class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
@Override
public String toString() {
return "Point(" + x + ", " + y + ")";
}
}
Point p = new Point(3, 4);
System.out.println(p); // Point(3, 4) - much more useful for debuggingOverriding equals() and hashCode() Together
The contract requires overriding both together - equal objects must produce the same hash code.
import java.util.Objects;
class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Point)) return false;
Point other = (Point) o;
return x == other.x && y == other.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
Point p1 = new Point(3, 4);
Point p2 = new Point(3, 4);
System.out.println(p1.equals(p2)); // true - compares field values now
java.util.Set<Point> points = new java.util.HashSet<>();
points.add(p1);
System.out.println(points.contains(p2)); // true - relies on both equals() AND hashCode()Best practices
- Always override equals() and hashCode() together - overriding just one breaks the contract that equal objects must have equal hash codes
- Use java.util.Objects.hash(field1, field2, ...) for a quick, reliable hashCode() implementation instead of writing the hashing logic by hand
- Override toString() on any class you plan to log or print for debugging - the default output is not useful
- Consider a record instead of a manual class when all you need is a simple immutable value type - it generates all three methods correctly for you
Runtime & versions
Java Virtual Machine. Feature availability depends on the language and runtime version used by your project; consult the official reference below.
Java Language Specification