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.equals to 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.equals before, 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:
UserInfo userInfo = CurrentUser.getUserInfo();
if (Objects.isNull(userInfo)) {
log.info("Please log in first");
return;
}
if (Objects.equals(userInfo.getId(), 888)) {
sendEmail(userInfo);
}After logging in with the admin account (id=888) and performing the operation, no email was received.
The UserInfo class is defined as:
@Data
public class UserInfo {
private Long id;
private String name;
private Integer age;
private String address;
}The problem lies in the type mismatch: the id field is Long, while the literal 888 is an int. Because the two types differ, Objects.equals returns false.
2. Methods for Equality Comparison
2.1 Using the == Operator
The fastest way to compare primitive values is the == operator.
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); // falseFor 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 Integer objects (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 == d4 being true, while values outside that range (e.g., 128) are not cached, making d5 == d6 false.
2.2 Using the equals Method
The == operator cannot compare the internal data of two distinct objects. For such cases, the equals method should be used.
String g = new String("abc");
String h = new String("abc");
System.out.println(g == h); // falseAlthough the references differ, the string contents are identical. The String class overrides equals to compare character arrays element‑by‑element, returning true when the contents match.
String e = "abc";
String f = "abc";
System.out.println(e.equals(f)); // true3. NullPointerException
Both == and equals can throw a NullPointerException when one operand is null. For example:
int a = 1;
Integer c = null;
System.out.println(a == c); // NullPointerExceptionSimilarly, invoking e.equals(f) when e is null throws an exception.
The solution is to perform a null check before calling equals, or use a utility method such as:
private static boolean equals(String e, String f) {
if (e == null) {
return f == null;
}
return e.equals(f);
}4. Purpose of Objects.equals
The Objects class in java.util provides helper methods, among which Objects.equals(Object a, Object b) is widely used.
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}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:
Integer a = 1;
long b = 1L;
System.out.println(Objects.equals(a, b)); // falseEven though the numeric values are equal, Objects.equals returns false because Integer.equals only 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.
System.out.println(Objects.equals(a, (int) b)); // true
System.out.println(Objects.equals(b, (long) a)); // trueIn summary, while Objects.equals is a handy null‑safe comparator, developers must be aware of type‑mismatch pitfalls, especially when dealing with primitive wrapper classes.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
