0

I have been developing for many years and have used many OOP languages over the past 30 years. I am still not sure if I have ever used polymorphism. I have searched and read but still don't understand the concept. Is it a feature of OOP or an implementation? Is it a combination of Abstraction, encapsulation, inheritance, Interfaces and Overriding? Maybe I have used it but don't know?

Can someone please point me to a clear definition and use cases? I just need a basic understanding of it and I am good to go.

Thanks in advance, Matt

1

4 Answers 4

2

Polymorphism is a very generic term, which basically just means "the ability of dealing with different types". That is more or less all that is common to all the different types of polymorphism.

The most widely-used and most well-known type of polymorphism is probably parametric polymorphism (also sometimes known as generics). It means pretty much what it says: it deals with a type as a parameter, i.e. as something that is not known at the definition site.

A prime example is a function for counting the number of items in a list. It does not actually need to know anything about the types of the items in the list, it is parametric in the item type:

def length[A](l: List[A]): Int = ???

or in Java

int length<A>(List<A> l) { return 0; /* Dummy implementation */ }

Another well-known type of polymorphism is bounded polymorphism, which is essentially parametric polymorphism but you put some restrictions on which types can be passed as parameters.

For example in C#:

class SortedList<T> where T : IComparable<T> { /* … */ }

This says that the items of a SortedList cannot just be any type, they can only be types that conform to the IComparable interface. Which makes sense, since in order to be able to keep the items of a sorted list sorted, we need to be able to compare them.

This is by no means an exhaustive list, we haven't talked about inclusion polymorphism, subtype polymorphism, F-bounded polymorphism, or the various forms of ad-hoc polymorphism (for example, Haskell's type classes).

None of these, however, are in any way related to OO.

3
  • Conform to the interface? More like implement (inherit) the interface. Nov 11, 2020 at 18:20
  • 1
    @Deduplicator - I feel it's OK to use "conform" in this context. Are you referring to the fact that the C# compiler requires you to explicitly implement the interface? The reason I say this is because, this fact is a detail specific to the concrete language (C#) and, in some sense, isn't really relevant to the general concept of bounded polymorphism. E.g., TypeScript will accept any type that has the right members (structural typing), so "conform" can be seen as a more generalized term (with its concrete meaning being language-dependent). Nov 11, 2020 at 18:45
  • @FilipMilovanović: and if Shapes is ever more than a pipe dream, it might not even be that anymore.
    – jmoreno
    Nov 11, 2020 at 19:33
1

Note first that the concept of polymorphism is not restricted to OOP; there are different kinds of polymorphism (I'll go through some of them here). The word polymorphism means "many forms", and as you'll see (I hope), it's rather appropriately chosen.

What it all comes down to, though, is simply the ability to call some function without specifying the exact concrete version (implementation) of the function to call. (I'll be focusing on functions, and ignore data structures for the purposes of this answer.) Some kinds of polymorphism are built into and/or enabled by the language, others you can design yourself in different ways. And you're right, it does involve abstraction.

Ad hoc polymorphism

Ordinary function overloading is an example of ad hoc polymorphism. If you have several overloads of what's conceptually the same function, those overloads are actually different concrete implementations (different concrete functions), that happen to share the same name.

Now, when you call an overload, you normally don't explicitly specify the concrete function to call. Instead, the compiler determines the concrete implementation to invoke based on the concrete types of the arguments passed (at the call site).

In other words, for the most common usage scenarios of an overloaded function, you can express (write) your client code in terms of an abstraction - a conceptual single function - without needlessly cluttering your code with type information. It's polymorphic in the sense that, as you write code, you are referring to what's conceptually a single function, but the compiler is substituting different forms of it in an ad hoc way (i.e. as necessary). This more readily observed in cases where two overloads take the same number of parameters, but the signatures differ in parameter types - the two calls look the same, but the compiler figures out what to call for you.

Parametric polymorphism

Think of C++ templates or C# generics. Roughly, type parameters let you write a function in a generic way, and have the compiler generate concrete versions based on the actual calls you made - and replace those calls with calls to the appropriate versions. (Or in the case of C#, it's the compiler, in conjunction with the runtime and the JITter.) Again, you have an abstraction (the idea of a generic function, and the syntax that supports that), and different concrete versions (different forms) of that function that you don't have to explicitly specify.

OOP

Here, polymorphism is supported by method call dispatch. When you call a method, you're not specifying a concrete function. A method call is an abstraction of a function call. The concrete method implementation to call (which is, under the hood, really just a function somewhere in memory) is determined based on the concrete type of the object that you used to call the method.

Note that inheritance is not necessary here, in principle. E.g., in JavaScript, you can use objects polymorphically, even if they aren't part of an inheritance hierarchy. This is not some strange quirk of JavaScript, this is how OOP is supposed to work:

var a = { someMethod: () => console.log('a') };
var b = { someMethod: () => console.log('b') };

[a, b].forEach(
  obj => obj.someMethod()
);

// Output:
a
b

It's a form of indirection; when you say obj.someMethod(), "someMethod" is treated as a token of some sort (the "message"), not as a call to a specific function, and there's an underlying mechanism that maps that token to a concrete function.

In case of JavaScript, the object is really a dictionary, and that token is literally the string that represents the name of the function, that is then used as a key (e.g., you can call the method via obj['someMethod']()). This is the fundamental underlying object model of JS (things get a bit more complicated when you add JavaScript's prototypal inheritance to the picture, but let's ignore that). I.e., the abstract method is represented by the string 'someMethod', and this same string is used as a key for every object that offers an implementation of that method.

Strongly typed languages require more structure, so you have the mechanism of static inheritance thrown into the picture, but the underlying principle is the same. E.g., in case of C++, in a polymorphic call to obj->someMethod(), "someMethod" is essentially converted to an offset into the vtable the object is associated with (basically, a table of pointers to concrete implementations). Here, the abstract method is represented by that vtable offset, and, again, for every object that offers an implementation, the offset corresponding to someMethod is the same number.

So, again, this lets you write your client code in terms of the abstract notion of a "method"; you don't need to specify the concrete function to call. Behind the scenes, an interpreter or a compiler converts your human-readable code into something that explicitly makes use of the underlying dispatching mechanism. This then further allows you to dynamically substitute different implementations of the same method (or different implementations of a collection of methods - an interface), and it all works. So now you have runtime polymorphism. In this sense, different objects that offer different implementations represent different forms of the same abstract interface.

If you've ever written code against an interface that was implemented by several different objects (i.e. if you made use of the idea of calling abstract methods on other objects), or if you ever inherited from a framework class (providing your own implementations of framework-mandated methods, letting it call you), then you've used this type of polymorphism.

1

The most common form of Polymorphism is where you have a heirachy of classes and you can substitute child classes for parent classes.

Polymorphism is also known as Subtyping

example heirachy

Here Ferry is the child class of the Boat class. With this we can write functions that deal in terms of Boats but are actually an instances of Ferry classes, for example:

class Boat {
public:
    void Accelerate() {
        accelerating = true;
    }
    
    virtual const char * GetName() = 0;
private:
    bool accelerating;
}

class Ferry : public Boat {
public:
    const char * GetName() override {
        return "Ferry Number 1";
    }
}

class SailBoat : public Boat {
public:
    const char * GetName() override {
        return "Sail boat 2";
    }
}

Boat* ferryA = new Ferry();
Boat* sailBoatB = new SailBoat();

void AccelerateBoat(Boat* target) {
    printf("The boat's name is %s\r\n", target->GetName());
    target->accelerate();
}

AccelerateBoat(ferryA);
AccelerateBoat(sailBoatB);

Here boat is an abstraction that represents the things all boats have in common.

Here is a great tutorial, see "3.4. Generalization and Specialization". http://people.cs.aau.dk/~normark/oop-csharp/html/notes/intro-oop_themes-phen-concepts-sect.html

0

I have searched and read but still don't understand the concept.

Yes, let's stick with the concept. And be concise... The first thing to take note of is that it applies to class trees: models that feature a base class and a number of other classes that all descend from that one base class.

As a base class, think Tree, Shape, Animal or Country. This base class is abstract. This means you cannot climb a tree, draw a shape, pet an animal or travel to a country in the sense that it will actually be an AppleTree, a Cirkel, a Dog or Germany.

So Tree, Shape, Animal or Country are abstract classes that will never be instantiated to become objects. There will only be instances of the descending classes. But... you can still define behavior for abstract classes:

Grow, Draw, Speak and GetLanguage.

This behavior will have to be implemented in the concrete classes, in the abstract base class it is just an interface that the descending classes will have to comply to.

The polymorphic quality is in two things:

  • the ability to implement the behavior differently for the different descendants;
  • the ability to treat any object of a concrete class as a generic object of the base class type.

So imagine you have a bunch of animal objects of which all you know is that they descend from Animal. Any of them could be a cat, a dog or a mouse. You want them to make noise so you call the Speak method on each of them. Now the dogs will respond with woof, the cat with miauw and the mouses with squeak. That is polymorphism at work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.