Incremental Java
Dynamic Dispatch

Dynamic and Static

When you hear the word dynamic associated with programming languages, it usually means "at runtime". "At runtime" means "at the time the program is running".

This is often contrasted with static. This means "at compile time". As you can already guess, compile-time means at the time that you are compiling a program.

There's a problem with the word static. It's used in too many places. For example, static variables have nothing to do with compile-time. In this case, static means relating to the class (as opposed to the object).

To be honest, they should simply get rid of the terms dynamic and static and use runtime and compile-time. It makes for far less confusion.

Is-A Relationship

Suppose we have some Java classes modelled after the animal kingdom. We have a class called Animal. We derive a class from that called Mammal. We derive a class from Mammal called Human.

This is a kind of hierarchy. It's called an inheritance hierarchy.

Suppose we declare a Human object called bob. We could say bob is a Human. We could also say bob is a Mammal (since Human is derived from Mammal). We could also say that bob is an Animal.

In fact, Java allows you to do the following:

   Human bob = new Human() ;
   Mammal bob2 = bob ;
   Animal bob3 = bob ;
Notice that bob, bob2, and bob3 are all object variables of different types, and all share a handle to the same Human object.

Declared vs. Actual Type

Let's look at the declaration again:
   Human bob = new Human() ;
   Mammal bob2 = bob ;
   Animal bob3 = bob ;
The declared type of bob is Human. The declared type of bob2 is Mammal. The declared type of bob3 is Animal.

As you can see, the declared type is static. That is, it's information we know at compile-time. After all, you're forced to write down the type of variables in a program before it's compiled.

However, the actual type of bob, bob2 and bob3 is Human.

The actual type is dynamic. This means "at runtime". While you can tell from the declaration above that bob, bob2 and bob3 is Human, in general, you can keep reassigning say bob3 to objects that have type Animal, Mammal or Human.

Declared Types Control Choice of Method

Even though bob, bob2 and bob3 share a handle to a Human object, they can't call all Human methods.

In particular, you can only call methods from your declared type. Thus, the declared type of bob2 is Mammal, then only Mammal methods can be used on bob2. You can't use methods that only appear in Human. You can use Animal methods, but only because Mammal inherites those methods anyway.

Suppose Animal defines method a1() and a2(), Mammal defines method b1() and b2(), and Human defines method h1() and h2().

You can only call methods a1(), a2(), b1(), and b2() on bob2 since the declared type of bob2 is Mammal.

Even though bob2 has actual type of Human, you can't call methods h1() and h2(). (Later on, we'll explain how to access those methods: it has to do with dynamic casting).

Actual Types Control Choice of Method

We know Mammal defines m1(). Suppose Human overrides this method, and redefines m1().

When you make the call bob2.m1(), does it call the Mammal version (i.e., does it call the method based on the declared type), or does it call the Human version which overrode the Mammal version (i.e., does it call the method based on the actual type).

Since this section is called Dynamic Dispatch, you should guess that the answer is that it bases the choice of method on the actual type, i.e., the runtime type. Thus, it picks the Human version.

Why the Restriction on Declared Types?

If we know that bob2 has a handle to a Human object, why can't we can't we call Human methods? Because we don't know that bob2 will always have a handle to a Human object. Suppose we reassign bob2 to a Mammal object. If we tried to call a Human method, the program would crash.

Therefore, Java takes the conservative approach, and only allows method calls based on the declared type.

Object Variables Can Be Assigned to Object Handles of Descendant Classes

You've declared an object variable. You wish to assign an object to this variable. What object type can you assign? Let's look at a simple assignment statemnt.
  x = y ;
You can do this assignment if the declared type of y is a descendant class of the declared type of x.

Notice I used declared types in both cases. This is why:

  Mammal m ;
  Animal h = new Human() ;
  // INVALID! Declared type of h not descendant 
  // class of declared type of m
  m = h ; 
Even though m can be assigned to a Human object (since Human is a descendant class of Animal), the compiler sees h, whose declared type is Animal.

The Java compiler assumes that h could hold an Animal object. Therefore, it doesn't permit this assignment.

Let's see what would happen if it did allow this assignment. Suppose m were assigned to h, an Animal object. If we call m.m1(), the program would crash because h has a handle to an Animal object which doesn't define m1(). m1() was defined in Mammal and doesn't appear in Animal.

Descendant Classes Only Add Methods

When you write a child (descendant) class, you can only add methods. Thus, when you do:
  Mammal m ;
  Human h = new Human() ;
  m = h ; 
The assignment statement is never a problem. We know that Human can handle all the methods than Mammal can. Even if h is a handle to an object from a descendant class of Human, then its methods must have at least the method that appear in Human which is a superset of the methods of Mammal.

Interfaces Also Have Dynamic Dispatch

Remember when we talked about interfaces? We said
  Drawable d1 = new Rectangle() ;
  System.out.println( "The area is: " + d1.getArea() ) ;
The declared type of d1 is Drawable. The actual type is Rectangle. It calls getArea() from Rectangle. In fact, there's no choice in this matter. Drawable is an interface, and has no method definitions to run!

When the call to d1.getArea() is made, the method run must be determined at runtime.