Methods of the Object class
Every class in Java inherits several methods from Object. Three of them are overridden most often: toString() turns an object into text, equals() compares objects, and hashCode() is needed by collections for fast lookup.
The default toString()
When an object is printed or joined with a string, Java calls its toString() method. The version from Object prints the class name and a hexadecimal number:
class User {
String name = "Anna";
}
void main() {
IO.println(new User());
}Output (the number after @ may differ from run to run):
Main$User@2698dc7Such text is useless to a human.
Your own toString()
Override toString(), and the object will print in a readable way — inside lists and in the debugger too:
class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{name=" + name + ", age=" + age + "}";
}
}
void main() {
User user = new User("Anna", 28);
IO.println(user);
IO.println("User: " + user);
}Output:
User{name=Anna, age=28}
User: User{name=Anna, age=28}== and equals()
For objects the == operator checks whether it is the same reference. The equals() method from Object does the same, so two objects with identical contents are considered different:
class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
void main() {
Point a = new Point(1, 2);
Point b = new Point(1, 2);
IO.println(a == b);
IO.println(a.equals(b));
}Output:
false
falseYour own equals()
To compare objects by contents, you override equals(). The method accepts an Object, so you check the type first and then the fields:
class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
return o instanceof Point p && x == p.x && y == p.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
void main() {
Point a = new Point(1, 2);
Point b = new Point(1, 2);
IO.println(a == b);
IO.println(a.equals(b));
}Output:
false
truehashCode()
hashCode() returns an integer — a "fingerprint" of the object. Collections like HashSet and HashMap compare fingerprints first and only then call equals(). The rule is simple: objects equal by equals() must have the same hashCode(). The easiest way is to build it from the same fields with Objects.hash(), as in the example above.
Common mistake
If you override equals() but forget hashCode(), equal objects get different fingerprints. A set will not find an element that is in it:
class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
return o instanceof Point p && x == p.x && y == p.y;
}
}
void main() {
Set<Point> points = new HashSet<>();
points.add(new Point(1, 2));
IO.println(points.contains(new Point(1, 2)));
}Output:
falseAlways override equals() and hashCode() together. Records (record) from the next lesson do this automatically.