DEV Community

loading...

What some people don't know about Polymorphism

pandaquests profile image pandaquests ・4 min read

This post was first published on my blog:
https://pleasefindencoded.blogspot.com/2020/04/post-79-what-some-people-dont-know.html

In the context of programming, polymorphism means - in abstract terms - when programming constructs or part of a code, e.g. classes or methods, can be applied to multiple Objects of different types.

Usually this is where the knowledge of most people ends. Most people don't know there are different kinds of polymorphism. In this post I'll explain the different types of polymorphism and discuss the advantages and disadvantages of each one of them:

Subtype-Polymorphism

If people talk about polymorphism, they usually mean this type of polymorphism. This is also the most simplest kind of polymorphism. When subtyping, an object of a subtype can be used everywhere where it's supertype is used. For example:

class Supertype {}
class Subtype extends Supertype {}
...
Supertype sup1 = new Supertype();
Supertype sup2 = new Subtype(); 
Enter fullscreen mode Exit fullscreen mode

In the example above, subtype can be used where supertype is used.

Another is example:

class MyObject1 {}
class MyObject2 {}

MyObject1 o1 = new MyObject1();
MyObject2 o2 = new MyObject2();
List<Object> obList = new ArrayList<>();
obList.add(o1);
obList.add(o2);
obList.add(2);
obList.add("hello");
Enter fullscreen mode Exit fullscreen mode

In the above example obList can take objects of different types - from custom objects to numbers to String.

Advantages

  • Subtype-polymorphism gives you flexibility

Disadvantages

  • It's hard to limit the flexibility
  • You lose type safety, because you only have limited control of which object is allowed to be added to a container and if you take an object out of a container, you have to cast it to it's original type in order to use it in a meaningful way

Parametic Polymorphism

Sometimes you want to reuse a class for different types but don't want to write for every type a class of their own. So, basically you only want to swap out certain parameters for a specific type. This is what parametic polymorphism is about.

When creating a class, you set a generic type T. Within the class you treat the generic type like any other type. When initializing you specify the generic type T to a specific type. For example:

public class MyObject1<T> {
    T obj;    
    void setObj(T obj) {
        this.obj = obj;
    }

    T getObj() {
        return this.obj;
    }
}
Enter fullscreen mode Exit fullscreen mode

Let's say you want to apply this class only to Integers, then you initialize it like the following:

MyObject1<Integer> object1 = new MyObject1<Integer>();
Enter fullscreen mode Exit fullscreen mode

On another instance you want to reuse the same class but for another type, then you initialize it with a different class:

MyObject1<String> object2 = new MyObject1<String>();
Enter fullscreen mode Exit fullscreen mode

You can use this technique not only for one type but also multiple types. For example:

public class KeyValue<K, V> {
    private K key;    
    private V value;        
    void setKeyValue(K key, V value) {
        this.key = key;
        this.value = value;
    }

    K getKey() {
        return this.key;
    }

    V getValue() {
        return this.value;
    }
}
Enter fullscreen mode Exit fullscreen mode

Advantages

  • Usually a much more powerful type analysis at compile time

Disadvantages

  • Parametric polymorphism offers less flexibility compared to subtype polymorphism
  • The generic parameter doesn't know about the attributes and methods of the type that will be applied to at the end

Limited parametric polymorphism

The limited parametric polymorphism builds upon the parametic polymorphism and enables you to limit the type parameter to a certain type by defining an upper bound for your type. This is useful when you want for example to build a container of different types and need to know the existence of certain attributes or methods beforehand.

For example you have a basket of different food products and want to know which of these elements is the oldest. Using limited parametric polymorphism, you would implement like this:

public interface Ageable {
    Date getBestBeforeDate();
}
Enter fullscreen mode Exit fullscreen mode
public class FoodBasket<T extends Ageable> {
    private T oldestFood;
    public void put(T food) {
        if (oldestFood == null ||
food.getBestBeforeDate().compareTo(oldestFood.getBestBeforeDate()) < 0) {
            oldestFood = food;
        }
    }

    public T getOldestFood() {
        return oldestFood;
    }
}
Enter fullscreen mode Exit fullscreen mode

By using extends we are creating an upper bound for our type. Thus we know that our type has to have the attributes or methods defined in the superclass resp. interface and can work them.

Let's say you want to use the basket for a specific kind of food and want to extend it with additional functionalities, the you would go about this:

public class Egg implements Ageable {
    private Date bestBeforeDate;
    Egg(Date bestBeforeDate) {
        this.bestBeforeDate = bestBeforeDate;
    }

    @Override
    public Date getBestBeforeDate() {
        return this.bestBeforeDate;
    }
}
Enter fullscreen mode Exit fullscreen mode
public class OldEggsBasket extends FoodBasket<Egg> {
    // additional functions
}
Enter fullscreen mode Exit fullscreen mode

Types can also implement multiple interfaces. The interfaces are seperated by an ampersand &:

public interface Printable {
    void print();
}
Enter fullscreen mode Exit fullscreen mode
public class FoodBasket<T extends Ageable & Printable> {
    private T oldestFood;
    public void put(T food) {
        if (oldestFood == null ||
 food.getBestBeforeDate().compareTo(oldestFood.getBestBeforeDate()) < 0) {
            oldestFood = food;
        }
        food.print();
    }
    //...
 }
Enter fullscreen mode Exit fullscreen mode

Advantages

  • Write reusable code, e.g. library classes, that are used by other programmers who can parameterize according to their need

Disadvantages

  • Less flexibility in comparison to the above mentioned kinds of polymorphism

Ad-hoc Polymorphism

Most developers probably know this concept already under a different name: overloading. With overloading you are providing the different methods with the same name for different signatures. The most common one is the plus (+) operator.

Advantages

  • Flexibility in naming of (overloaded) methods

Disadvantages

  • No code reuse. Therefore isn't really considered polymorphism like the other kinds of polymorphism

I am interested whether you know more advantages and disadvantages for each of the types of polymorphism.

Discussion

pic
Editor guide