Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding Static and Dynamic Dispatch in Java Method Invocation

Tech May 9 3

Polymorphism and Method Selection

Polymorphism allows objects to be treated as instances of their parent class rather than their actual class. In Java, this behavior manifests primarily through method overloading and method overriding. The mechanism by which the Java Virtual Machine (JVM) selects the specific method implementation to execute is known as dispatch.

Static Dispatch and Method Overloading

Static dispatch occurs during compilation. The compiler determines which method to call based on the declared type (static type) of the variable, rather than the actual type of the object instantiated at runtime. This mechanism is the backbone of method overloading.

Consider the following implementation demonstrating static dispatch:

class DispatchExample {

    static class Entity {}
    static class Player extends Entity {}
    static class Monster extends Entity {}

    public void performAction(Entity e) {
        System.out.println("Action performed on Entity");
    }

    public void performAction(Player p) {
        System.out.println("Action performed on Player");
    }

    public void performAction(Monster m) {
        System.out.println("Action performed on Monster");
    }

    public static void main(String[] args) {
        Entity hero = new Player();
        
        DispatchExample example = new DispatchExample();
        example.performAction(hero);
    }
}

Although the actual object instantiated is a Player, the variable hero is declared as an Entity. Consequently, the compiler binds the call to performAction(Entity e), resulting in the output "Action performed on Entity".

Overload Resolution Priority

When dealing with inheritance hierarchies, the compiler selects the most specific overload available based on the static type. It traverses the hierarchy upwards, looking for the closest matching parent type.

class HierarchyResolution {

    static class Base {}
    static class LevelOne extends Base {}
    static class LevelTwo extends LevelOne {}

    public void process(Base b) {
        System.out.println("Processing Base");
    }

    public void process(LevelOne l) {
        System.out.println("Processing LevelOne");
    }

    public static void main(String[] args) {
        HierarchyResolution resolver = new HierarchyResolution();
        LevelTwo obj = new LevelTwo();
        
        // The compiler matches LevelTwo to LevelOne (closest parent match)
        resolver.process(obj); 
    }
}

In this scenario, LevelTwo does not have a specific method, so the compiler selects process(LevelOne) over process(Base) because LevelOne is the immediate parent of LevelTwo.

Dynamic Dispatch and Method Overriding

Dynamic dispatch occurs at runtime and is responsible for implementing polymorphism through method overriding. The JVM determines the actual type of the object on the heap and invokes the corresponding method implementation.

class DynamicDispatchDemo {

    static abstract class Animal {
        abstract void makeSound();
    }

    static class Dog extends Animal {
        @Override
        void makeSound() {
            System.out.println("Dog barks");
        }
    }

    static class Cat extends Animal {
        @Override
        void makeSound() {
            System.out.println("Cat meows");
        }
    }

    public static void main(String[] args) {
        Animal pet = new Dog();
        pet.makeSound(); // Outputs: Dog barks

        pet = new Cat();
        pet.makeSound(); // Outputs: Cat meows
    }
}

Despite the variable pet being of type Animal, the JVM invokes the method belonging to the actual object type (Dog or Cat). This lookup process follows a specific algorithm:

  1. Retrieve the actual class C of the object referenced on the stack.
  2. Search C for a method matching the name and descriptor.
  3. If found and accessible, use it; if access is denied, throw IllegalAccessError.
  4. If not found, search the parent classes recursively upwards.
  5. If no method is found after searching the hierarchy, throw AbstractMethodError.

Single vs. Multiple Dispatch

Dispatch strategies are categorized by the "quantities" used for selection: the receiver of the method and the method arguments.

  • Single Dispatch: The method is chosen based on a single quantity, typically the runtime type of the receiver.
  • Multiple Dispatch: The method is chosen based on multiple quantities, such as the runtime types of all arguments.

Java supports static multiple dispatch (overloading considers the static types of arguments) but only dynamic single dispatch (overriding considers only the runtime type of the receiver). Complex scenarios requiring dynamic multiple dispatch (like the Visitor pattern) often necessitate workarounds involving double-checked logic or pattern matching implementations.

Tags: JavaJVM

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.