Why Does Objects.equals Sometimes Return False? Uncover Java’s Equality Pitfalls
This article explains why Java's Objects.equals can yield unexpected false results, compares different equality‑checking methods, shows how type mismatches and null values cause pitfalls, and provides practical code examples and solutions for safe object comparison.
Preface
While reviewing a colleague's code I discovered that using
Objects.equalsto compare two values returned a result different from the expectation, which sparked my interest.
I originally expected the comparison to return true, but it actually returned false.
Having encountered similar pitfalls with
Objects.equalsbefore, I felt it was necessary to record this issue and share it.
1. The Scene
Assume a requirement: if the currently logged‑in user is the designated system administrator, send an email. The admin has no special flag; its user id is 888 in development, testing, and production.
The implementation looks straightforward:
<code>UserInfo userInfo = CurrentUser.getUserInfo();
if (Objects.isNull(userInfo)) {
log.info("Please log in first");
return;
}
if (Objects.equals(userInfo.getId(), 888)) {
sendEmail(userInfo);
}
</code>After logging in with the admin account (id=888) and performing the operation, no email was received.
The
UserInfoclass is defined as:
<code>@Data
public class UserInfo {
private Long id;
private String name;
private Integer age;
private String address;
}
</code>The problem lies in the type mismatch: the
idfield is
Long, while the literal
888is an
int. Because the two types differ,
Objects.equalsreturns false.
2. Methods for Equality Comparison
2.1 Using the == Operator
The fastest way to compare primitive values is the
==operator.
<code>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
System.out.println(d2 == a); // true
System.out.println(d1 == d2); // false
</code>For primitive types the
==operator compares values directly. For wrapper classes,
==compares object references, which can yield false even when the values are equal.
When an Integer is compared with an int , automatic unboxing occurs, so the values are compared.
Two distinct
Integerobjects (e.g., created with
new) are not guaranteed to be
==even if they hold the same value.
Comparing two Integer objects with == checks whether they reference the same memory address.
Integer values between -128 and 127 are cached, so
Integer d3 = 1; Integer d4 = 1;results in
d3 == d4being true, while values outside that range (e.g., 128) are not cached, making
d5 == d6false.
2.2 Using the equals Method
The
==operator cannot compare the internal data of two distinct objects. For such cases, the
equalsmethod should be used.
<code>String g = new String("abc");
String h = new String("abc");
System.out.println(g == h); // false
</code>Although the references differ, the string contents are identical. The
Stringclass overrides
equalsto compare character arrays element‑by‑element, returning true when the contents match.
<code>String e = "abc";
String f = "abc";
System.out.println(e.equals(f)); // true
</code>3. NullPointerException
Both
==and
equalscan throw a
NullPointerExceptionwhen one operand is
null. For example:
<code>int a = 1;
Integer c = null;
System.out.println(a == c); // NullPointerException
</code>Similarly, invoking
e.equals(f)when
eis
nullthrows an exception.
The solution is to perform a null check before calling
equals, or use a utility method such as:
<code>private static boolean equals(String e, String f) {
if (e == null) {
return f == null;
}
return e.equals(f);
}
</code>4. Purpose of Objects.equals
The
Objectsclass in
java.utilprovides helper methods, among which
Objects.equals(Object a, Object b)is widely used.
<code>public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
</code>This method first checks reference equality, then null‑safety, and finally delegates to
a.equals(b). It avoids NullPointerExceptions and is convenient for safe comparisons.
5. Pitfalls of Objects.equals
A hidden pitfall appears when the two arguments have different wrapper types. Example:
<code>Integer a = 1;
long b = 1L;
System.out.println(Objects.equals(a, b)); // false
</code>Even though the numeric values are equal,
Objects.equalsreturns false because
Integer.equalsonly returns true when the argument is also an
Integer. The same behavior exists for
Long.equals,
Byte.equals,
Short.equals,
Double.equals,
Float.equals,
Boolean.equals, and
Character.equals.
When using Objects.equals , ensure that both parameters are of the same type; otherwise equal numeric values may still be considered unequal.
To fix the issue, cast one argument to the other's type before comparison, or simply use the
==operator for primitive values.
<code>System.out.println(Objects.equals(a, (int) b)); // true
System.out.println(Objects.equals(b, (long) a)); // true
</code>In summary, while
Objects.equalsis a handy null‑safe comparator, developers must be aware of type‑mismatch pitfalls, especially when dealing with primitive wrapper classes.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.