skip to content

When checking an object's type, when should you use getClass() comparison versus instanceof, and what is the trade-off?

level: middleimportance: should knowfreq 40%

answer

  1. instanceof = is-a, includes subtypes, null-safe
  2. getClass() == = exact type only, NPE on null
  3. equals symmetry: getClass() preserves it, instanceof can break it
  4. proxies/subclasses fail getClass() check
  5. Class.isInstance is the reflective null-safe instanceof

basics

~20 s

instanceof returns true for subclasses too, so use it when subtypes should count as a match. getClass() == OtherClass requires the exact runtime type, ignoring subclasses. equals() methods often use getClass() so a subclass is not treated as equal to its parent.

solid answer

~40 s

Both inspect runtime type but answer different questions. obj instanceof Type is true when obj's runtime type is Type or any subtype (and it is null-safe, returning false for null). Comparing obj.getClass() == Type.class is true only for the exact runtime class, excluding subclasses, and throws NPE if obj is null. The classic place this matters is equals(): using getClass() makes the symmetry contract hold even with subclasses, because a subclass instance will never compare equal to a parent instance and vice versa, whereas instanceof can break symmetry when a subclass adds fields. The trade-off is Liskov substitutability: instanceof lets subtypes participate (good for polymorphism), while getClass() enforces exact-type equality (good for value-type correctness but rejects proxies/subclasses). Choose instanceof for behavioral checks and getClass() when exact identity matters.

code

java · 15 lines
java
class A {}
class B extends A {}

A a = new B();
boolean isA      = (a instanceof A);          // true (subtype matches)
boolean exactA   = (a.getClass() == A.class); // false (runtime type is B)
boolean exactB   = (a.getClass() == B.class); // true

// null behavior
Object n = null;
boolean inst = (n instanceof A);   // false, no exception
// n.getClass();                   // would throw NullPointerException

// reflective, null-safe equivalent of instanceof
boolean refl = A.class.isInstance(a); // true

go deeper

for a junior

Know that instanceof also matches subclasses and is null-safe, while getClass() checks the exact type.

for a middle

Explain the equals symmetry consequence and pick the right tool for behavioral vs exact-identity checks.

for a senior

Discuss Liskov substitution trade-offs, proxy/subclass pitfalls in ORM contexts, and the reflective isInstance/isAssignableFrom equivalents.

for a principal

Frame the choice in terms of value vs entity semantics and substitutability, and advise teams on equals strategy under proxying frameworks and inheritance hierarchies.

## Two ways to ask 'what type is this?' When you need to test an object's runtime type you generally reach for one of two tools, and they answer subtly different questions. ### `instanceof` ```java if (obj instanceof Number) { ... } ``` - True when `obj`'s **runtime type is `Number` or any subtype** (Integer, Double, a custom subclass...). - **Null-safe:** `null instanceof X` is always `false` (no exception). - Models the **'is-a'** relationship — exactly what polymorphism wants. - Java 16+ adds **pattern matching**: `if (obj instanceof String s) { use s; }` binds and casts in one step. ### `getClass()` comparison ```java if (obj.getClass() == Number.class) { ... } // almost never true: Number is abstract if (obj.getClass() == Integer.class) { ... } // exact match only ``` - True only when the **runtime class is exactly** the compared class — **subclasses do NOT match**. - **Throws `NullPointerException`** if `obj` is null (it's a method call). - Models **exact-type identity**, not 'is-a'. ### The canonical case: `equals()` The `equals` contract requires **symmetry**: `a.equals(b)` must equal `b.equals(a)`. Consider a `Point` and a subclass `ColorPoint` that adds a color field. - If `Point.equals` uses **instanceof**, a `Point` may consider a `ColorPoint` equal (it only checks x,y), but `ColorPoint.equals` also checks color and rejects the `Point` — **symmetry breaks**. - If `equals` uses **`getClass()` comparison**, a `Point` and a `ColorPoint` are never equal in either direction, so symmetry holds. This is why many style guides (and Java's own value-like classes) prefer `getClass()` in `equals`. The cost: it **rejects subclasses and dynamic proxies** (e.g. Hibernate/Spring proxies, whose `getClass()` is a generated subclass), which can cause surprising 'not equal' results for ORM-managed entities — there, `instanceof` (or `Hibernate.getClass()`) is often preferred. ### Trade-off summary | Concern | `instanceof` | `getClass() ==` | |---|---|---| | Subclasses match? | yes | no | | Null handling | false (safe) | NPE | | Honors Liskov substitution | yes | no | | equals symmetry with subclasses | can break | holds | | Works with proxies/subclasses | yes | no | ### Rule of thumb - Use **`instanceof`** for **behavioral / capability** checks (can I treat this as a `Comparable`? is this a `Collection`?) and wherever subtypes should count. - Use **`getClass()` equality** when **exact type identity** is required — most commonly inside `equals()` for value types, or when distinguishing a class from its subclasses on purpose. - For pure type membership without an instance you can also call `SomeClass.isInstance(obj)` (the reflective, null-safe equivalent of `instanceof`) or `SomeClass.isAssignableFrom(other.getClass())`.

  • Why can instanceof break the equals symmetry contract?
    If a subclass adds state and overrides equals to check it, the parent (using instanceof) may consider the subclass equal while the subclass rejects the parent, so a.equals(b) != b.equals(a). getClass() comparison avoids this by requiring identical runtime types.
  • What is the reflective, null-safe equivalent of instanceof?
    SomeClass.isInstance(obj). It returns true if obj is non-null and assignable to SomeClass (including subtypes), and false for null, mirroring instanceof but usable when the type is a Class object known only at runtime.

saying these in an interview costs you the question

  • Claiming getClass() == matches subclasses
  • Forgetting instanceof is null-safe while getClass() throws NPE on null
  • Always preferring instanceof in equals (breaks symmetry with field-adding subclasses)
  • Using getClass() equality on ORM proxy objects and being surprised they never match

context