Polymorphism is the third essential feature of OOP.
Polymorphism means many forms – an object can behave in different forms and the correct behavior will be evaluated during run-time only.
Taking the concept of inheritance further, an object of derived class can be used as its own type or as a type of its base class. Although this statement might appear as incorrect – why would Java allow one type to behave like another type, but if we think in a object oriented manner it will make sense.
Inheritance is making a base class (generic) into a more specific derived class. So, derived class object still posses all the generic behavior of base class. By doing upcasting we are loosing specifics of a derived class but retaining generics of base class.
Let’s take this example
Although this is the desired output, but looking at the method park() it doesn’t make sense. In the park() method we are invoking move() of Vehicle but actually move() of Car is invoked.
Connecting a method call to method body is called binding. When this binding happens during compile time, its called static binding. When this binding happens during run time (based on type of object), its called dynamic binding or runtime binding.
In the above example, even when Car object is upcasted to Vehicle in park(), it still knows its type information and behave in polymorphic manner to produce desired output.
Downcasting is opposite of upcasting. Here we cast a generic object into more specific one. While Upcasting is always safe and happens implicitly, Downcasting requires explicit type casting operator and throws a runtime ClassCastException if the instance to be downcasted does not belong to the correct subclass.