Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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@2698dc7

Such 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
false

Your 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
true

hashCode()

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:

false

Always override equals() and hashCode() together. Records (record) from the next lesson do this automatically.

Thanks for reading!

Donate to the site — we work hard for you.