Search code examples
compilationpolymorphismoverridingruntimeoverloading

Java overloading at compile time versus overriding at runtime and polymorphism


could someone explain what is meant when they say that overloading occurs at compile time whereas overriding occurs at runtime? My understanding is overloading occurs within a class where a method's name is the same but the parameters differ. The return type may or may not be different. Overriding occurs between classes, or more specifically, between a super class and a descendant. Here the signature must match. Polymorphism occurs between classes that are descendants of the same super class. Here the return and the parameters are the same but the implementation is different

Why are same method names within a class resolved at compile time and same method names between classes resolved at runtime?


Solution

  • Given the following code...

    Class Base
       Method Example(char x)
       Method Example(int x)
    
    Base inst = NEW Base
    inst.Example(1)
    

    The compiler will see those methods as distinct methods because their signatures make them uniquely identifiable. So it knows the method call will go to the 2nd method in the class.

    But things are a little more complicated than that because of subclasses...

    Class Base
       Method Example(char x)
       Method Example(int x)
    
    Class Ext1 Extends Class Base
       Method Example(char x)
    
    Class Ext2 Extends Class Base
       Method Example(int x)
    
    Base inst = NULL
    
    if (something) {
      inst = NEW Base
    } else if (something_else) {
      inst = NEW Ext1
    } else {
      inst = NEW Ext2
    }
    
    inst.Example(1)
    

    Now although the compiler can still work out that the Example method which takes an int will be called at compile time, it cannot know which of the 3 classes will have been created, so it doesn't know the actual concrete method that will be called

    This is where the vtable comes in. Taking the first example again but simulating how a vtable gets involved...

    Class Base
       VTable[0] = Method Example(char x)
       VTable[1] = Method Example(int x)
    
    Base inst = NEW Base
    inst.VTable[1](1)
    

    The compiler has turned what looks to us like a direct method call into an indirect call via the vtable. It is this method lookup process that allows overriding to work...

    Class Base
       VTable[0] = Method Example(char x)
       VTable[1] = Method Example(int x)
    
    Class Ext1 Extends Class Base
       VTable[0] = Method Example(char x)
    
    Class Ext2 Extends Class Base
       VTable[1] = Method Example(int x)
    
    Base inst = NULL
    
    if (something) {
      inst = NEW Base
    } else if (something_else) {
      inst = NEW Ext1
    } else {
      inst = NEW Ext2
    }
    
    inst.VTable[1](1)
    

    So whereas overloading can be handled at compile time, overriding happens at runtime thanks to the vtable.