DEV Community

Thellu
Thellu

Posted on

Understanding Java Overloading & Static Dispatch

1️⃣ Static Type vs. Actual (Runtime) Type

In Java, every variable has:

  • Static type (compile-time type) --- what the compiler sees and uses to check method calls.
  • Actual type (runtime type) --- the real class of the object stored in that variable.

Example:

Human man = new Man(); 
Enter fullscreen mode Exit fullscreen mode
  • Human → static type\
  • Man → actual (runtime) type

Static type is fixed at compile time. Actual type is only known at
runtime.


2️⃣ Overloading Is Based on Static Type

Take this code:

public class StaticDispatch {
    static abstract class Human {}
    static class Man extends Human {}
    static class Woman extends Human {}

    public void sayHello(Human people) {
        System.out.println("hello,people");
    }

    public void sayHello(Man man) {
        System.out.println("hello,man");
    }

    public void sayHello(Woman woman) {
        System.out.println("hello,woman");
    }

    public static void main(String[] args) {
        Human man = new Man();
        Human woman = new Woman();
        StaticDispatch sr = new StaticDispatch();
        sr.sayHello(man);
        sr.sayHello(woman);
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

hello,people
hello,people
Enter fullscreen mode Exit fullscreen mode

Why?
Because overloading resolution happens at compile time --- the
compiler only sees that the variables man and woman are declared as
Human. It doesn't care that at runtime the actual objects are Man
and Woman.

If you want the compiler to choose the more specific overload, you must
cast:

sr.sayHello((Man) man);
sr.sayHello((Woman) woman);
Enter fullscreen mode Exit fullscreen mode

Now the static types at the call site are Man and Woman, so the
specific overloads will be chosen.


3️⃣ Overload Resolution Priority

Sometimes, multiple overloaded methods can match. Java applies a
specific priority order
to decide which one to use.

Let's look at a classic example:

public class A {
    public static void sayHi(Object arg)       { System.out.println("object"); }
    public static void sayHi(int arg)          { System.out.println("int"); }
    public static void sayHi(long arg)         { System.out.println("long"); }
    public static void sayHi(Character arg)    { System.out.println("Character"); }
    public static void sayHi(char arg)         { System.out.println("char"); }
    public static void sayHi(char... arg)      { System.out.println("char..."); }
    public static void sayHi(Serializable arg) { System.out.println("Serializable"); }

    public static void main(String[] args) {
        sayHi('a');
    }
}
Enter fullscreen mode Exit fullscreen mode

Run with all methods

Output → char

Because 'a' is literally a char.

Comment out sayHi(char)

Output → int
Reason: 'a' can be promoted to its numeric value 97.

Comment out sayHi(int)

Output → long
Reason: widening: char -> int -> long.

Comment out sayHi(long)

Output → Character
Reason: autoboxing 'a' to Character.

Comment out sayHi(Character)

Output → Serializable
Reason: Character implements Serializable.

Comment out sayHi(Serializable)

Output → Object
Reason: autobox to Character then upcast to Object.

Comment out sayHi(Object)

Output → char...
Reason: fallback to varargs.

👉 Priority summary:

Primitive exact match >
Widening primitive conversion >
Autoboxing to wrapper >
Autoboxing to implemented interfaces >
Autoboxing to Object >
Varargs
Enter fullscreen mode Exit fullscreen mode

4️⃣ Why It Matters

This might seem academic, but understanding overloading resolution
and the compile-time vs runtime type distinction helps you:

  • Avoid surprising method calls.
  • Write cleaner APIs (be careful with overloads that can cause ambiguity).
  • Debug confusing NoSuchMethodError or ambiguous method errors.

For interview prep or deep JVM knowledge, this topic often comes up ---
especially the static vs runtime type concept and how overload
resolution works.


⚡️ Key Takeaways

  • Overloading uses compile-time types. The JVM doesn't do dynamic dispatch for overloaded methods.
  • Casting affects overload resolution (because it changes the static type at the call site).
  • Autoboxing & varargs are fallback mechanisms with well-defined priority.
  • Be mindful with overloaded APIs --- too many similar signatures can cause unexpected calls.

💬 Have you encountered unexpected overload resolution in real projects
or interviews? Share your experiences below!

Top comments (0)