Fundamentals 15 min read

Why Objects.equals Can Mislead You: Hidden Pitfalls and How to Avoid Them

This article examines why Java's Objects.equals may return unexpected results, explores the differences between ==, equals, and Objects.equals, highlights null‑pointer risks, and reveals common type‑mismatch pitfalls with practical code examples and solutions.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Why Objects.equals Can Mislead You: Hidden Pitfalls and How to Avoid Them

Introduction

While reviewing a teammate's code I discovered a case where Objects.equals returned false even though the two values looked identical, prompting a deeper investigation.

1. Incident Scene

A requirement checks whether the currently logged‑in user is the system administrator (userId = 888) and sends an email if true. The naive implementation uses:

UserInfo userInfo = CurrentUser.getUserInfo();
if (Objects.isNull(userInfo)) {
    log.info("请先登录");
    return;
}
if (Objects.equals(userInfo.getId(), 888)) {
    sendEmail(userInfo);
}

The UserInfo class defines id as Long. Because the literal 888 is an int, the comparison fails.

2. Methods for Equality Comparison

2.1 Using ==

For primitive types the == operator compares values directly. For wrapper objects it compares references, which can be surprising:

int a = 1;
int b = 1;
byte c = 1;
Integer d1 = new Integer(1);
Integer d2 = new Integer(1);
System.out.println(a == b); // true
System.out.println(a == c); // true
System.out.println(a == d1); // true (auto‑unboxing)
System.out.println(d1 == d2); // false (different objects)
When an Integer is compared with an int , auto‑unboxing occurs, so the values are compared.

Wrapper objects also use a constant pool for values between -128 and 127, which explains why Integer i1 = 1; Integer i2 = 1; yields true while Integer i3 = 128; Integer i4 = 128; yields false.

2.2 Using equals Method

The Object.equals method only checks reference equality:

public boolean equals(Object obj) {
    return (this == obj);
}

String overrides equals to compare character arrays element‑by‑element, allowing value equality for distinct objects:

public boolean equals(Object anObject) {
    if (this == anObject) return true;
    if (anObject instanceof String) {
        String anotherString = (String) anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char[] v1 = value;
            char[] v2 = anotherString.value;
            for (int i = 0; i < n; i++) {
                if (v1[i] != v2[i]) return false;
            }
            return true;
        }
    }
    return false;
}

Thus "abc".equals(new String("abc")) returns true even though the references differ.

3. NullPointerException

Both == and equals can trigger NullPointerException when one operand is null and auto‑unboxing is required:

int a = 1;
Integer c = null;
System.out.println(a == c); // NullPointerException (auto‑unboxing null)
String e = null;
String f = "abc";
System.out.println(e.equals(f)); // NullPointerException

Guarding against null before calling equals solves the problem:

private static boolean equals(String e, String f) {
    if (e == null) return f == null;
    return e.equals(f);
}

4. Purpose of Objects.equals

The utility method in java.util.Objects safely compares two objects:

public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}

It first checks reference equality, then handles the a == null case, and finally delegates to a.equals(b). This avoids NullPointerException while preserving logical equality.

5. Pitfalls of Objects.equals

When the two arguments have different wrapper types, Objects.equals may return false even if the numeric values are equal:

Integer a = 1;
long b = 1L;
System.out.println(Objects.equals(a, b)); // false
System.out.println(a == b); // true (auto‑unboxing)

The reason is that Integer.equals only returns true if the argument is also an Integer. The same applies to Long.equals, Byte.equals, etc.

Therefore, when using Objects.equals , ensure both parameters are of the same type; otherwise the method will incorrectly report inequality.

Common scenarios where this pitfall appears include:

Comparing Long user IDs with Integer values.

Comparing Byte status codes with Integer values.

Comparing Double amounts with Integer zero checks.

Solutions are to cast one operand to the other's type or to use primitive comparison with == after unboxing.

IDEA warning for misuse of equals
IDEA warning for misuse of equals
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javabest practicesnullpointerexceptionequalityObjects.equals
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.