skip to content

You can write `someObject.staticField` to reach a static member through an instance. Why is this allowed but discouraged?

level: middleimportance: should knowfreq 55%

answer

  1. object is ignored; resolves to the declared type at compile time
  2. misleading: looks per-instance, is class-shared
  3. static is NOT polymorphic (binds to declared type)
  4. works even on a null reference (no NPE)
  5. prefer ClassName.member; IDEs warn

basics

~20 s

Java lets you reach a static member through an object reference, but it really just resolves to the class. It's discouraged because it misleads readers into thinking the value is per-object when it's actually shared by the whole class. Use ClassName.member instead.

solid answer

~40 s

Static members can be accessed through an instance reference (`obj.staticField`, `obj.staticMethod()`), but the compiler ignores the object and resolves the access against the *declared type* of the reference at compile time. The object is not used at all, which leads to two surprises: it's misleading (it reads like per-instance state when the member is class-wide and shared), and with subtype references it can resolve to the wrong class because static access is *not* polymorphic. A notorious gotcha: `obj.staticMethod()` still executes even when `obj` is `null`, because no instance is dereferenced. For these reasons style guides and IDEs flag instance-qualified static access; the idiomatic and clear form is `ClassName.member`. The feature exists mostly for historical/Java-language-spec reasons, not because it's useful.

go deeper

for a junior

Knows the preferred form is ClassName.member and that accessing statics via an object is discouraged.

for a middle

Explains that the object is ignored and the access resolves to the declared type, making it misleading; cites the null-reference and non-polymorphism surprises.

for a senior

Connects it to static hiding vs overriding and compile-time binding; treats IDE/style warnings as enforceable conventions.

for a principal

Frames it as a JLS legacy/grammar artifact, weighs backward-compat constraints, and sets team-wide lint policy to forbid it.

## Setup A **static member** belongs to the class (one shared copy / no instance needed). The normal way to access it is by the class name: `Math.PI`, `Integer.parseInt(...)`. Java *also* lets you reach a static member through an **instance reference**: ```java Integer i = 5; int x = i.parseInt("10"); // legal, but parseInt is static! ``` This compiles. But it's misleading and discouraged. Here's why. ## What actually happens: the object is ignored When you write `i.parseInt("10")`, the compiler sees that `parseInt` is static and **rewrites the call to `Integer.parseInt("10")`**. The reference `i` is used only to determine the *type* (`Integer`) at **compile time**; the actual object is never touched. Consequences: ### 1) It reads as per-instance state but isn't ```java order.taxRate // looks like this order's tax rate ``` If `taxRate` is static, every order shares one value — changing it for one 'changes it' for all. A reader scanning the code is misled. `Order.taxRate` makes the class-wide nature obvious. ### 2) Static access is not polymorphic — it binds to the declared type ```java class A { static String who() { return "A"; } } class B extends A { static String who() { return "B"; } } A ref = new B(); String r = ref.who(); // "A", not "B"! ``` Because static resolution uses the *declared* type `A` (compile-time), not the runtime object `B`, you get `A`. Instance method overriding would have given `B`. This surprise is a direct trap of instance-qualified static access. ### 3) It "works" even on `null` ```java Integer n = null; n.parseInt("7"); // does NOT throw NPE ``` Since the object is never dereferenced (only its compile-time type matters), a `null` reference still calls the static method successfully. That's confusing — every other `null.member` access throws `NullPointerException`. ## The recommended form Always qualify with the class: ```java Integer.parseInt("10"); Math.PI; A.who(); ``` IDEs (IntelliJ, Eclipse), `checkstyle`, and many style guides emit a warning like "Static member accessed via instance reference" and offer to rewrite it to class-qualified form. ## Why does the language allow it at all? Mostly historical: the Java Language Specification permits it for grammar uniformity (a field/method access expression doesn't have to know statically whether the member is static). It survives for backward compatibility, not because it's good style. Treat it as a legacy quirk to avoid.

  • Given `A ref = new B();` with a static `who()` overridden-looking method in both, what does `ref.who()` return and why?
    It returns `"A"`. Static methods are not overridden but hidden, and access resolves against the *declared* type `A` at compile time, ignoring the runtime `B` object.
  • Why doesn't `nullRef.staticMethod()` throw a NullPointerException?
    Because the call is resolved to `ClassName.staticMethod()` at compile time and the object is never dereferenced; only the reference's compile-time type is used.

saying these in an interview costs you the question

  • Thinking the instance somehow selects which copy is used (there's only one copy)
  • Expecting `ref.staticMethod()` to dispatch to the runtime type (it uses declared type)
  • Expecting a NullPointerException when the reference is null
  • Believing it's idiomatic just because it compiles

context