How do you compare two object references for value equality safely when either might be null, and why is x.equals(y) risky?
answer
- x.equals(y) NPEs when x (receiver) is null
- Objects.equals(a,b): both null=true, one null=false, else a.equals(b)
- argument null is fine: "h".equals(null)=false
- constant-first trick: "OK".equals(status)
- == is null-safe but tests identity, not value
basics
~10 sCalling x.equals(y) throws NullPointerException if x is null. Use Objects.equals(x, y) instead — it handles nulls (both null returns true, one null returns false) and otherwise calls x.equals(y).
solid answer
~50 sequals() is an instance method, so x.equals(y) dereferences x — if x is null you get a NullPointerException before any comparison happens. The standard fix is java.util.Objects.equals(x, y), a null-safe static helper: it returns true if both are null, false if exactly one is null, and otherwise delegates to x.equals(y). This is the idiomatic way to compare possibly-null references and is also what you use field-by-field inside a hand-written equals() implementation. A common defensive trick when comparing against a constant is to put the non-null operand first: "OK".equals(status) never NPEs even if status is null. Note this is about logical equality; if you only need identity, == is inherently null-safe (null == x just evaluates without throwing). Don't confuse the two: == won't compare contents, and Objects.equals won't tell you if they're the same object.
code
java · 8 linesString status = null;
// status.equals("ACTIVE"); // NPE: null receiver
boolean a = Objects.equals(status, "ACTIVE"); // false, no NPE
boolean b = "ACTIVE".equals(status); // false, constant-first trick
boolean c = Objects.equals(null, null); // true
boolean d = "x".equals(null); // false (null argument is fine)go deeper
Knows that x.equals(y) can NPE on a null x and that Objects.equals(x,y) is the safe alternative.
Explains the both-null/one-null semantics, uses Objects.equals when writing equals(), and knows == is null-safe but identity-only.
Distinguishes receiver-null vs argument-null, applies constant-first and Objects.equals consistently, and ties it into correct equals/hashCode authoring.
Sets null-handling and equality conventions across a codebase (e.g. nullability annotations, Optional vs null, Objects utilities) and reasons about where null should be impossible by design.
## The hazard `equals()` is an **instance method** declared on `Object`. To call `x.equals(y)`, the JVM must first **dereference** `x` (find the object `x` points to). If `x` is `null`, there is no object — so you get a **`NullPointerException` (NPE)** *before* any comparison even runs. ```java String x = null; x.equals("hello"); // throws NullPointerException ``` Note that the *argument* being null is fine — `"hello".equals(null)` simply returns `false` (the `equals` contract requires returning false for a null argument). Only a null **receiver** (the thing before the dot) is the problem. ## The fix: `Objects.equals` `java.util.Objects.equals(a, b)` is a static helper that is **null-safe**. Its logic is: ```java public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); } ``` Reading that: - If `a` and `b` are the *same reference* (including **both null**) → `true`. - Else if `a` is null (and `b` isn't) → the `&&` short-circuits → `false`. - Else → delegate to `a.equals(b)` safely (we know `a` isn't null). So: ```java Objects.equals(null, null); // true Objects.equals(null, "x"); // false Objects.equals("x", null); // false Objects.equals("x", "x"); // true ``` This is the **idiomatic** way to compare two references that might be null, and it's exactly what you call **field by field** when hand-writing an `equals()` method (each object field may be null). ## The constant-first trick When comparing a possibly-null variable against a known non-null constant, put the **constant first** so the receiver is never null: ```java if ("ACTIVE".equals(status)) { ... } // safe even if status == null ``` Many teams treat this as a style rule (and some IDE inspections suggest it). `Objects.equals` works too and reads more symmetrically. ## Don't confuse with `==` `==` is **inherently null-safe** because it doesn't call a method — `null == x` just evaluates to a boolean without dereferencing. But `==` tests **identity**, not value, so it answers a *different* question. Use: - `Objects.equals(x, y)` → null-safe **value** comparison. - `x == y` → **identity** comparison (and the only correct tool for primitives, where there's no null and no `equals`). ## Bonus: `Objects.requireNonNull` is a different tool Don't confuse `Objects.equals` with `Objects.requireNonNull(x)`, which *throws* if `x` is null (a guard for fail-fast validation), or `Objects.hashCode(x)` / `Objects.hash(...)` used in the matching `hashCode()`.
- What does "hello".equals(null) return, and why no exception?It returns false. The receiver "hello" is non-null, so no dereference fails; the equals contract simply mandates returning false for a null argument.
- Inside a hand-written equals(), why use Objects.equals on each field?Because any object-typed field may itself be null. Objects.equals(this.f, other.f) compares them without risking an NPE when a field is null, keeping the implementation correct and concise.
saying these in an interview costs you the question
- Thinking equals() never throws (null receiver does)
- Believing a null argument throws (it returns false)
- Using == to dodge NPE when you actually need value comparison
- Confusing Objects.equals with Objects.requireNonNull
- Assuming Objects.equals compares identity