Static vs Dynamic Binding

Difficulty: Intermediate

Question

What is the difference between static (early) binding and dynamic (late) binding? Which methods are bound at compile time?

Answer

Binding is the process of connecting a method call to the actual code that will run. The whole question is: when does that connection get made, at compile time or at run time? Once you see this, many "why did it print that?" questions become easy.

In static binding, also called early binding, the compiler decides which method to call, using the declared type of the reference. Nothing about the actual object is considered. In dynamic binding, also called late binding, the decision is deferred until the program runs, and the JVM looks at the actual class of the object the reference points to. That is the machinery behind run-time polymorphism.

Which calls are statically bound in Java? Static methods, because they belong to the class, not to any object. Private methods, because they cannot be overridden so there is nothing to choose between. Final methods, because they also cannot be overridden. Constructors, which are not inherited. Overloaded methods, since the overload is chosen by the compiler from the argument types. And field access, which never participates in polymorphism: p.name always uses the declared type of p. Everything else, meaning ordinary non-final, non-private instance methods, is dynamically bound.

How does the JVM do dynamic binding efficiently? Each class has a method table, similar to a C++ vtable, holding pointers to the implementation of each virtual method. An object carries a hidden pointer to its class, so an invokevirtual instruction looks up the slot in that table, which is only slightly slower than a direct call. The JIT compiler can often optimise it further using inline caches and devirtualisation when it sees only one implementation being used.

A mistake I see often is assuming that a parent reference calling a static method will trigger the child's version. It does not. In the example below, A x = new B() and x.s() prints A.s because the compiler looks at the type of x, which is A, and static methods are not overridden but hidden. The call x.i() prints B.i because i is an instance method dispatched using the real object. The second example shows overload resolution: the call f(ref) chooses f(A) because ref is declared as A, even though the object is a B. Overloads are chosen statically, even when overriding is dynamic.

Why do the two coexist? Static binding is faster and allows more compiler optimisation; dynamic binding gives flexibility. C++ makes you opt in to dynamic binding with the virtual keyword; without it, C++ binds statically by pointer type. Java flips the default: all instance methods are virtual unless private, final, or static. Python is fully dynamic, and every attribute lookup is resolved at run time.

One more subtle point: the final keyword on a method is sometimes used as a micro-optimisation hint. Modern JVMs make this largely unnecessary, so use final for design intent, not for speed.

Code examples

Static methods are hidden, instance methods are overridden

class A {
    static void s() { System.out.println("A.s"); }
    void i()        { System.out.println("A.i"); }
}
class B extends A {
    static void s() { System.out.println("B.s"); }
    @Override void i() { System.out.println("B.i"); }
}

public class BindingDemo {
    public static void main(String[] args) {
        A x = new B();
        x.s();
        x.i();
    }
}

s() is bound at compile time using the declared type A; i() is bound at run time using the actual type B.

Overloads are chosen by reference type

class Animal {}
class Dog extends Animal {}

class Show {
    void f(Animal a) { System.out.println("f(Animal)"); }
    void f(Dog d)    { System.out.println("f(Dog)"); }
}

public class OverloadBinding {
    public static void main(String[] args) {
        Animal ref = new Dog();
        new Show().f(ref);
        new Show().f(new Dog());
    }
}

The compiler only knows ref as Animal, so f(Animal) is chosen even though the object is a Dog.

Key points

Concepts covered

binding, dynamic-dispatch, static-methods, vtable