🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
Polymorphism
This page was created by Stephanie on 2023-06-12. Last edited by Wikiadmin on 2026-07-29.

Polymorphism lets you work with objects through a common superclass while each subclass supplies its own behavior; use it when a collection or association can contain several specialized types, such as different kinds of Fruit.

Start with an inheritance model

Polymorphism builds on UML inheritance. In this example, Fruit is the superclass and Apple, Orange, Banana, Pear, and Pineapple are subclasses.

A Country can export Fruit. The resulting association can therefore contain objects of different subclass types. For example, malaysia.ExportsTheseFruits may contain an Apple, an Orange, and a Banana, even though the association is treated as a collection of Fruit.

The superclass gives you a common type. Polymorphism lets you ask each object to perform common behavior without first writing type checks such as fruit is Apple or fruit is Orange.

Define behavior on the superclass

Define an operation on Fruit when every kind of fruit can answer the same question. Mark the operation virtual so that subclasses can replace its implementation.

In this example, the default answer is that the seeds are noticeable when you eat the fruit:

public partial class Fruit
{
    public virtual bool HasSeedsYouNoticeWhenYouEat()
    {
        return true;
    }
}

The method name and return type form the contract shared by all fruit. Code that works with Fruit can call this method without knowing whether the current object is an Apple, Orange, or Banana.

Override behavior in a subclass

Override the virtual operation only where the subclass needs a different result. A Banana uses the same operation but provides its own answer:

public partial class Banana
{
    public override bool HasSeedsYouNoticeWhenYouEat()
    {
        return false;
    }
}

The override keyword means that Banana replaces the inherited Fruit implementation for Banana objects. Other Fruit subclasses continue to use the default implementation unless they also override the operation.

Use the common type

You can now iterate over the Country association as Fruit and let each object select the correct implementation at runtime:

List<Fruit> fruitWithNoticeableSeeds = new List<Fruit>();
List<Fruit> fruitWithoutNoticeableSeeds = new List<Fruit>();

foreach (Fruit fruit in malaysia.ExportsTheseFruits)
{
    if (fruit.HasSeedsYouNoticeWhenYouEat())
        fruitWithNoticeableSeeds.Add(fruit);
    else
        fruitWithoutNoticeableSeeds.Add(fruit);
}

For a Banana, the call resolves to Banana.HasSeedsYouNoticeWhenYouEat() and the object is added to fruitWithoutNoticeableSeeds. For a Fruit subclass that does not override the method, the call resolves to the Fruit implementation and the object is added to fruitWithNoticeableSeeds.

This is the practical value of polymorphism: the loop handles all Fruit objects through one common interface, while each subtype retains its own behavior.

Make the general class abstract when it must not exist on its own

An abstract class is a class that defines a general concept but must not be instantiated directly. In the fruit model, a real fruit must be an Apple, Pear, Orange, Banana, or Pineapple; it must never be only Fruit.

When Fruit is abstract, the compiler reports an error if code tries to create a Fruit instance. This expresses the model's intent: Fruit is an abstraction used to share properties, associations, and operations among its subclasses.

Treat a superclass with subclasses as abstract when the superclass does not represent an object that should exist independently. In the example, Fruit is the general category; the concrete subclasses represent the objects you create.

Apply this pattern

  1. Model a general superclass and its specialized subclasses. See UML Inheritance for the inheritance model.
  2. Put an operation on the superclass when callers need the same question or action for every subtype.
  3. Provide a default virtual implementation when that default is valid for some subclasses.
  4. Add an override in each subclass whose behavior differs.
  5. Iterate over collections and associations using the superclass type, such as Fruit.
  6. Mark the superclass abstract when it is only a generalization and must not be created directly.

See also