Java

Lesson 03

equals, hashCode, and value objects

Use small value objects or records for identity values so equality, hashing, and validation stay together.

Good Code

src/main/java/dev/review/user/UserId.java
public record UserId(UUID value) {
    public UserId {
        Objects.requireNonNull(value, "value");
    }

    public static UserId parse(String rawValue) {
        return new UserId(UUID.fromString(rawValue));
    }
}

Bad Code

src/main/java/User.java
public class User {
    private String id;
    private String displayName;

    @Override
    public boolean equals(Object other) {
        if (!(other instanceof User user)) {
            return false;
        }

        return displayName.equals(user.displayName);
    }
}

Review Notes

What to review

Good Code

The good version models identity as a value object. Java records give consistent equality and hash behavior, while the constructor keeps invalid values out.

Bad Code

The bad version compares users by display name and overrides equals without a matching hashCode. Collections such as HashSet and HashMap can behave incorrectly.

Takeaways

  • If a type can be used as a map key or set member, equality and hash behavior must be deliberate and consistent.