Search code examples
javapolymorphismdynamic-bindingstatic-binding

What is the difference between compile time linking and run time linking?


I am currently reading a book and stuck at following code:

public class TestAnimals {
    public static void main (String [] args ) {
        Animal a = new Animal();
        Animal b = new Horse();

        a.eat(); // Runs the Animal version of eat()
        b.eat(); // Runs the Horse version of eat()

    }
}

class Animal {
    public void eat() {
        System.out.println("Generic animal eating generically");
    }
}

class Horse extends Animal {
    public void eat() {
        System.out.println("Horse eating hay, oats, horse treats");
    }

    public void buck() {
    }
}

Please have a look at the commented lines.

The book goes on to say "To reiterate, the compiler looks only at the reference type, not the instance type". Really? Had this been the case, both a.eat() and b.eat() would produce same result, as they (a and b) have same reference type (which is Animal).

Also to me this seems to be the compile time binding because virtual keyword has not been used but in the book results are of run time binding. I am so confused at this point. Any help would be greatly appreciated.


Solution

  • The compiler does indeed look only at the statically known type, not the actual runtime type of the instance - after all, Java is a statically typed language. As a matter of fact, in all but the most trivial cases, the compiler can't even know the runtime type of an object reference (as to solve this problem for the general case, it would have to solve undecidable problems).

    The point the book is trying to make there is that this snippet would fail to compile:

    b.buck();
    

    Because b is of (compile-time) type Animal and Animal doesn't have a buck() method. In other words, Java (like C++), will verify at compile time whether the method call makes any sense, based on the information it has about the type of the variable.

    Now the reason the book's results correspond to runtime binding is precisely because you have runtime binding at that call spot: in Java (unlike in C++), all non-static methods are virtual by default.

    Thus, there's no need for a virtual keyword that would allow you to explicitly opt in to polymorphism semantics (as you would in C++ and C#, for example). Instead, you can only prevent any further overrides of your methods by individually marking them as final or marking their containing class as final (if the latter is applicable in your case).