Why are static methods NOT polymorphic? Demo with a subcl… — Cracked Java
// Object-Oriented Programming · Polymorphism in Practice
MidTrickTheoryAmazon

Why are static methods NOT polymorphic? Demo with a subclass.

Static methods are bound to the class at compile time, not to an instance at runtime — so the reference type, not the object's runtime type, picks which method runs. This is called static dispatch (or method hiding when a subclass declares a same-signature static method) and is the most common interview gotcha around polymorphism.

Demo

class Parent {
    static String who() { return "Parent"; }
    String greet() { return "hi from " + who(); }
}

class Child extends Parent {
    static String who() { return "Child"; }       // hides, not overrides
}

Parent p = new Child();
System.out.println(p.who());     // "Parent"  <- reference type wins
System.out.println(((Child) p).who()); // "Child"

Child c = new Child();
System.out.println(c.greet());   // "hi from Parent"  <-- !!

The last line is the kicker. Even though we called greet() on a Child, greet itself calls who() — and inside Parent.greet the receiver of who() is the class Parent. There's no vtable lookup to redirect it.

Why the JVM works this way

Static methods have no this. The bytecode is invokestatic, which takes a fully-qualified Class.method reference at the constant pool and jumps directly there — no dispatch table, no receiver lookup. The class is hardcoded into the call site by the compiler.

Compare to instance dispatch:

invokevirtual:  load receiver -> read class pointer -> vtable[slot] -> jump
invokestatic:   jump directly to resolved method

There is literally no mechanism by which invokestatic could pick the subclass implementation; the subclass isn't even part of the instruction.

Static methods aren't overridden — they're hidden

class Parent { static void log() { System.out.println("p"); } }
class Child extends Parent { static void log() { System.out.println("c"); } }  // legal — but no @Override

You can declare a same-signature static in a subclass; the compiler accepts it. But you cannot mark it @Override (the annotation explicitly rejects statics) because the subclass version hides the parent version when accessed through the subclass — it does not replace it via a vtable slot.

When you actually want polymorphic class-level behavior

Use an instance method on a singleton, an enum, or pass a Supplier<T> / strategy. Anything where the call goes through a receiver gets the dispatch you want.

Mark your status