One of the great strengths of object oriented programming is the ability to use different objects through the same interface while each behaves in its own unique way. This is polymorphism meaning “many forms”.
Polymorphism allows the expression of common behavior between types of objects which have similar traits. Inheritance can be thought of as one of the mechanisms we can use to achieve polymorphism in our code via the use of a hierarchy. However there are other ways to achieve polymorphism besides inheritance, ways which move away from the old OOP ways towards a protocol based approach.
There are three types of polymorphism; the first two probably more familiar than the last.
Parametric polymorphism is what’s commonly called generics. We enable polymorphic function/class etc through type parameter.
Subtype polymorphism is classic object-oriented polymorphism where one class (OOP concept) subclassing another class.
Ad-hoc polymorphism is function/operator overloading. Function can denote a number of distinct and potentially heterogeneous implementations depending on the type of argument(s) to which it is applied.
Static vs dynamic polymorphism:
Static polymorphism means the ability to have several functions, all called the same thing, which the compiler can choose between at Compile-Time depending on the arguments passed, and achieved through method overloading (decisions area made at compile-time via static or early binding). It can also mean that we can get different method implementations by casting the same object to different types.
Dynamic polymorphism is polymorphism that happens at run-time and it achieved via method overriding (though dynamic or late binding). The system has to run the program and, when a decision needs to be made about which method to run, it must look at the actual object in memory (regardless of the type of the reference, which may be a cast) and act appropriately.
Most programmers use the word polymorphism to refer to dynamic polymorphism.