Its about the ability to decide which version of the method to be invoked * during runtime *. Means, at runtime whatever the actual object is being pointed to by the reference variable "a", would be invoked.
During compile time, it is not throwing any error because,
A reference variable to the parent/super class variable can hold the objects of any of its child/sub class. Thats why,
is allowed and compiled without any error.
Since because the method invoked with respect to the super class variable "a" has the method, the next line is compiled without an error. Does not matter whatever the reference variable "a" is assigned to, the statement invoking the method "act" with respect to "a" would be allowed because "a" is of type "A" and the class "A" has the method.
Try this code,
The above code would definitely compile without any errors. Only at runtime, it checks for the object being pointed by the reference variable "a" and tries to invoke the method "act()" wrt to that object. At this time it finds the refernece variable is not pointing to, rather not assigned any actual object reference and it cannot invoke any method on a null reference. So you get the NullPointerException (NPE) at the runtime and NOT during the compile time.
it looks like you have everything you need to know right now, and the compiler could make some "compile time" decisions. When you split them up, perhaps you don't even know who is going to write the "new B()" part ...
we don't know at compile time whether the argument is an instance of class A or some subclass. That means it has to be a runtime decision to invoke the method from class A or B or some other that we've never heard of.
A good question is never answered. It is not a bolt to be tightened into place but a seed to be planted and to bear more seed toward the hope of greening the landscape of the idea. John Ciardi