How a method call is dispatched

JVM · lesson 18 of 34 · 4 min read

Five kinds of call site, five opcodes, and one vtable lookup that picks the body at run time.

Open this lesson in the learning hub

Key points

  • javac chooses the opcode from the declared type of the receiver. There are four, plus a special case for lambdas.
  • invokestatic for static calls; invokespecial for constructors, private methods and super calls.
  • invokevirtual and invokeinterface look the real method up on the object at run time. That is dynamic dispatch.
  • HotSpot uses a per-class vtable, so a virtual call is one array lookup - and the JIT usually inlines it away entirely.
  • A lambda compiles to invokedynamic. The first execution asks a bootstrap method to spin the implementing class.
  • Fields are never virtual. A field access is resolved from the declared type, which is why hiding a field is not overriding it.

Example

public class Main {

    interface Payment { String label(); }

    static class Card implements Payment {
        public String label() { return "card"; }
    }

    static class Amex extends Card {
        @Override public String label() { return "amex"; }
    }

    static int twice(int n) { return n * 2; }              // invokestatic

    public static void main(String[] args) {
        Card asClass = new Amex();                         // declared Card, really an Amex
        Payment asInterface = new Amex();                  // declared Payment, really an Amex

        System.out.println("invokestatic     twice(21)          -> " + twice(21));
        System.out.println("invokespecial    new Card()         -> the constructor, never overridden");
        System.out.println("invokevirtual    asClass.label()    -> " + asClass.label());
        System.out.println("invokeinterface  asInterface.label() -> " + asInterface.label());
        System.out.println();
        System.out.println("declared type picked the opcode : " + Card.class.getSimpleName());
        System.out.println("runtime class picked the body   : " + asClass.getClass().getSimpleName());
        System.out.println();

        Payment lambda = () -> "lambda";
        System.out.println("invokedynamic spins a class the first time this line runs:");
        System.out.println("  " + lambda.getClass().getName());
        System.out.println("  label() -> " + lambda.label());
    }
}

The declared type chooses the opcode; the object chooses the method.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the JVM course, and every lesson in it is listed on the JVM contents page.