DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Languages
  4. OOP Concepts for Beginners: What Is Polymorphism

OOP Concepts for Beginners: What Is Polymorphism

Polymorphism can be an elusive concept to pin down, so let's take a look at what it is and what the different types are.

Thorben Janssen user avatar by
Thorben Janssen
·
Dec. 28, 17 · Tutorial
Like (29)
Save
Tweet
Share
27.99K Views

Join the DZone community and get the full member experience.

Join For Free

The word polymorphism is used in various contexts and describes situations in which something occurs in several different forms. In computer science, it describes the concept that objects of different types can be accessed through the same interface. Each type can provide its own, independent implementation of this interface. It is one of the core concepts of object-oriented programming (OOP).

If you’re wondering if an object is polymorphic, you can perform a simple test. If the object successfully passes multiple is-a or instanceof tests, it’s polymorphic. As I’ve described in my post about inheritance, all Java classes extend the class Object. Due to this, all objects in Java are polymorphic because they pass at least two instanceof checks.

Different Types of Polymorphism

Java supports 2 types of polymorphism:

  • static or compile-time
  • dynamic

Static Polymorphism

Java, like many other object-oriented programming languages, allows you to implement multiple methods within the same class that use the same name but a different set of parameters. That is called method overloading and represents a static form of polymorphism.

The parameter sets have to differ in at least one of the following three criteria:

  • They need to have a different number of parameters, e.g. one method accepts 2 and another one 3 parameters.
  • The types of the parameters need to be different, e.g. one method accepts a String and another one a Long.
  • They need to expect the parameters in a different order, e.g. one method accepts a String and a Long and another one accepts a Long and a String. This kind of overloading is not recommended because it makes the API difficult to understand.

In most cases, each of these overloaded methods provides a different but very similar functionality.

Due to the different sets of parameters, each method has a different signature. That allows the compiler to identify which method has to be called and to bind it to the method call. This approach is called static binding or static polymorphism.

Let’s take a look at an example.

A Simple Example for Static Polymorphism

I use the same CoffeeMachine project as I used in the previous posts of this series. You can clone it at https://github.com/thjanssen/Stackify-OopInheritance.

The BasicCoffeeMachine class implements two methods with the name brewCoffee. The first one accepts one parameter of type CoffeeSelection. The other method accepts two parameters, a CoffeeSelection, and an int.

public class BasicCoffeeMachine {
    // ...
    public Coffee brewCoffee(CoffeeSelection selection) throws CoffeeException {
        switch (selection) {
        case FILTER_COFFEE:
            return brewFilterCoffee();
        default:
            throw new CoffeeException(
                "CoffeeSelection ["+selection+"] not supported!");
        }   
    }

    public List brewCoffee(CoffeeSelection selection, int number) throws CoffeeException {
        List coffees = new ArrayList(number);
        for (int i=0; i<number; i++) {
            coffees.add(brewCoffee(selection));
        }
        return coffees;
    }
    // ...
}


Now when you call one of these methods, the provided set of parameters identifies the method which has to be called.

In the following code snippet, I call the method only with a CoffeeSelection object. At compile time, the Java compiler binds this method call to the brewCoffee(CoffeeSelection selection) method.

BasicCoffeeMachine coffeeMachine = createCoffeeMachine();
coffeeMachine.brewCoffee(CoffeeSelection.FILTER_COFFEE);


If I change this code and call the brewCoffee method with a CoffeeSelection object and an int, the compiler binds the method call to the other brewCoffee(CoffeeSelection selection, int number) method.

BasicCoffeeMachine coffeeMachine = createCoffeeMachine();
List coffees = coffeeMachine.brewCoffee(CoffeeSelection.ESPRESSO, 2);


Dynamic Polymorphism

This form of polymorphism doesn’t allow the compiler to determine the executed method. The JVM needs to do that at runtime.

Within an inheritance hierarchy, a subclass can override a method of its superclass. That enables the developer of the subclass to customize or completely replace the behavior of that method.

It also creates a form of polymorphism. Both methods, implemented by the super- and subclass, share the same name and parameters but provide different functionality.

Let’s take a look at another example from the CoffeeMachine project.

Method Overriding in an Inheritance Hierarchy

The BasicCoffeeMachine class is the superclass of the PremiumCoffeeMachine class.

Both classes provide an implementation of the brewCoffee(CoffeeSelection selection) method.

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class BasicCoffeeMachine extends AbstractCoffeeMachine {
    protected Map beans;
    protected Grinder grinder;
    protected BrewingUnit brewingUnit;

    public BasicCoffeeMachine(Map beans) {
        super();
        this.beans = beans;
        this.grinder = new Grinder();
        this.brewingUnit = new BrewingUnit();

        this.configMap.put(CoffeeSelection.FILTER_COFFEE, new Configuration(30, 480));
    }

    public List brewCoffee(CoffeeSelection selection, int number) throws CoffeeException {
        List coffees = new ArrayList(number);
        for (int i=0; i<number; i++) {
            coffees.add(brewCoffee(selection));
        }
        return coffees;
    }

    public Coffee brewCoffee(CoffeeSelection selection) throws CoffeeException {
        switch (selection) {
        case FILTER_COFFEE:
            return brewFilterCoffee();
        default:
            throw new CoffeeException("CoffeeSelection ["+selection+"] not supported!");
        }
    }

    private Coffee brewFilterCoffee() {
        Configuration config = configMap.get(CoffeeSelection.FILTER_COFFEE);

        // grind the coffee beans
        GroundCoffee groundCoffee = this.grinder.grind(this.beans.get(CoffeeSelection.FILTER_COFFEE), config.getQuantityCoffee());

        // brew a filter coffee
        return this.brewingUnit.brew(CoffeeSelection.FILTER_COFFEE, groundCoffee, config.getQuantityWater());
    }

    public void addBeans(CoffeeSelection selection, CoffeeBean newBeans) throws CoffeeException {
        CoffeeBean existingBeans = this.beans.get(selection);
        if (existingBeans != null) {
            if (existingBeans.getName().equals(newBeans.getName())) {
                existingBeans.setQuantity(existingBeans.getQuantity() + newBeans.getQuantity());
            } else {
                throw new CoffeeException("Only one kind of beans supported for each CoffeeSelection.");
            }
        } else {
            this.beans.put(selection, newBeans);
        }
    }
}
import java.util.Map;

public class PremiumCoffeeMachine extends BasicCoffeeMachine {
    public PremiumCoffeeMachine(Map beans) {
        // call constructor in superclass
        super(beans);

        // add configuration to brew espresso
        this.configMap.put(CoffeeSelection.ESPRESSO, new Configuration(8, 28));
    }

    private Coffee brewEspresso() {
        Configuration config = configMap.get(CoffeeSelection.ESPRESSO);

        // grind the coffee beans
        GroundCoffee groundCoffee = this.grinder.grind(this.beans.get(CoffeeSelection.ESPRESSO), config.getQuantityCoffee());

        // brew an espresso
        return this.brewingUnit.brew(
            CoffeeSelection.ESPRESSO, groundCoffee, config.getQuantityWater());
    }

    public Coffee brewCoffee(CoffeeSelection selection) throws CoffeeException {
        if (selection == CoffeeSelection.ESPRESSO)
            return brewEspresso();
        else
            return super.brewCoffee(selection);
    }
}


If you read the post about the OOP concept inheritance, you already know the two implementations of the brewCoffee method. The BasicCoffeeMachine only supports the CoffeeSelection.FILTER_COFFEE. The brewCoffee method of the PremiumCoffeeMachine class adds support for CoffeeSelection.ESPRESSO. If it gets called with any other CoffeeSelection, it uses the keyword super to delegate the call to the superclass.

Late Binding

When you want to use such an inheritance hierarchy in your project, you need to be able to answer the following question: which method will the JVM call?

That can only be answered at runtime because it depends on the object on which the method gets called. The type of the reference, which you can see in your code, is irrelevant. You need to distinguish three general scenarios:

  1. Your object is of the type of the superclass and gets referenced as the superclass. So, in the example of this post, a BasicCoffeeMachine object gets referenced as a BasicCoffeeMachine.
  2. Your object is of the type of the subclass and gets referenced as the subclass. In the example of this post, a PremiumCoffeeMachine object gets referenced as a PremiumCoffeeMachine.
  3. Your object is of the type of the subclass and gets referenced as the superclass. In the CoffeeMachine example, a PremiumCoffeeMachine object gets referenced as a BasicCoffeeMachine.

Superclass Referenced as the Superclass

The first scenario is pretty simple. When you instantiate a BasicCoffeeMachine object and store it in a variable of type BasicCoffeeMachine, the JVM will call the brewCoffee method on the BasicCoffeeMachine class. So, you can only brew a CoffeeSelection.FILTER_COFFEE.

// create a Map of available coffee beans
Map beans = new HashMap();
beans.put(CoffeeSelection.FILTER_COFFEE,

new CoffeeBean("My favorite filter coffee bean", 1000));

// instantiate a new CoffeeMachine object
BasicCoffeeMachine coffeeMachine = new BasicCoffeeMachine(beans);

Coffee coffee = coffeeMachine.brewCoffee(CoffeeSelection.FILTER_COFFEE);


Subclass Referenced as the Subclass

The second scenario is similar. But this time, I instantiate a PremiumCoffeeMachine and reference it as a PremiumCoffeeMachine. In this case, the JVM calls the brewCoffee method of the PremiumCoffeeMachineclass, which adds support for CoffeeSelection.ESPRESSO.

// create a Map of available coffee beans
Map beans = new HashMap();
beans.put(CoffeeSelection.FILTER_COFFEE,

new CoffeeBean("My favorite filter coffee bean", 1000));
beans.put(CoffeeSelection.ESPRESSO,

new CoffeeBean("My favorite espresso bean", 1000));

// instantiate a new CoffeeMachine object
PremiumCoffeeMachine coffeeMachine = new PremiumCoffeeMachine(beans);

Coffee coffee = coffeeMachine.brewCoffee(CoffeeSelection.ESPRESSO);


Subclass Referenced as the Superclass

This is the most interesting scenario and the main reason why I explain dynamic polymorphism in such details.

When you instantiate a PremiumCoffeeMachine object and assign it to the BasicCoffeeMachine coffeeMachine variable, it still is a PremiumCoffeeMachine object. It just looks like a BasicCoffeeMachine.

The compiler doesn’t see that in the code, and you can only use the methods provided by the BasicCoffeeMachine class. But if you call the brewCoffee method on the coffeeMachine variable, the JVM knows that it is an object of type PremiumCoffeeMachine and executes the overridden method. This is called late binding.

// create a Map of available coffee beans
Map beans = new HashMap();
beans.put(CoffeeSelection.FILTER_COFFEE,

new CoffeeBean("My favorite filter coffee bean", 1000));

// instantiate a new CoffeeMachine object
BasicCoffeeMachine coffeeMachine = new PremiumCoffeeMachine(beans);

Coffee coffee = coffeeMachine.brewCoffee(CoffeeSelection.ESPRESSO);


Summary

Polymorphism is one of the core concepts in OOP languages. It describes the concept that different classes can be used with the same interface. Each of these classes can provide its own implementation of the interface.

Java supports two kinds of polymorphism. You can overload a method with different sets of parameters. This is called static polymorphism because the compiler statically binds the method call to a specific method.

Within an inheritance hierarchy, a subclass can override a method of its superclass. If you instantiate the subclass, the JVM will always call the overridden method, even if you cast the subclass to its superclass. That is called dynamic polymorphism.

Object-oriented programming Polymorphism (computer science) Concept (generic programming) Object (computer science) Java (programming language)

Published at DZone with permission of Thorben Janssen, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Choosing the Right Framework for Your Project
  • Create a REST API in C# Using ChatGPT
  • Public Key and Private Key Pairs: Know the Technical Difference
  • How Chat GPT-3 Changed the Life of Young DevOps Engineers

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: