For enquiries call:

Phone

+1-469-442-0620

HomeBlogProgrammingPolymorphism in Java

Polymorphism in Java

Published
05th Sep, 2023
Views
view count loader
Read it in
0 Mins
In this article
    Polymorphism in Java

    Java is simply an object-oriented language, meaning it organizes code into various reusable components called objects (e.g. think of these as real-life objects). The language comes with a very rich standard library that provides the developer with a vast array of various pre-built classes and optimized methods, enabling them to build some robust and efficient applications quickly and easily. In this article, we will talk about Polymorphism (an OOP – “Object Oriented Programming” concept) in Java.

    Java comes with a very strict syntax and strong type-checking which ensure the code integrity and also reduces the likelihood of any errors. Its extensive toolset includes the Java Development Kit (popularly called as JDK), and various IDEs (Integrated Development Environments). Like Eclipse and IntelliJ IDEA, IDEs empower today’s developers with many powerful debugging tools, profiling, and also testing capabilities.

    What is Polymorphism in Java?

    Let us begin with understanding polymorphism in Java. Polymorphism is a prime fundamental concept prevalent in object-oriented programming and is one of the key features of the widely used Java language. It allows objects of different classes to be taken as types of a common superclass or interface. Java Programming training will help you become an ace programmer with the most in-demand programming courses.

    In the Java programming language, polymorphism is easily achieved with the implementation of method overriding and method overloading:

    • Method Overriding: It occurs when a subclass provides its own implementation of a method that is already defined in its superclass.
    • Method Overloading: It involves defining multiple methods in the same class with the same method name but different parameter lists.

    Polymorphism Promotes: 

    • Code Reusability
    • Flexibility
    • Modularity

    It allows for much more generic programming and also simplifies the process of code maintenance, as the changes made to the superclass or interface can also propagate to all implementing classes. Additionally, it facilitates the creation of frameworks and libraries that can work with different types of objects without requiring specific knowledge of their concrete implementations.

    Why use Polymorphism in Java?

    There are a plethora of reasons to use polymorphism in Java. Some of them are mentioned below:

    • Code Reusability: Polymorphism allows for the creation of objects of different classes to be treated in a uniform manner, which promotes code reusability and also modularity. By programming to a common interface or superclass, you can write a generic code that can work with multiple different objects.
    • Flexibility and Extensibility: Polymorphism grants the capability to you to write code that can easily accommodate new classes or also subclasses without the requirement of any significant modifications. You can thus introduce new classes that implement the same interface or extend the same superclass, and the existing code can seamlessly interact with these new additions.
    • Maintainability: Polymorphism helps in simplification of code maintenance by the reduction of the need for multiple conditional statements or type-checking. Changes made to the superclass or interface can also propagate to all implementing classes, ensuring consistency and reducing the risk of errors.
    • Frameworks and Libraries: Polymorphism is very essential for the creation of frameworks and libraries that need to work with many diverse objects. By defining the common interfaces, these tools can interact with various implementations, providing a standardized way of accessing functionality and promoting interoperability.
    • Polymorphic Behavior: Polymorphism allows for the creation of different objects to exhibit different behaviors based on their specific implementations of methods. This dynamic behavior thus results in flexibility in runtime and enables more complex and also specialized operations to be performed based on the actual type of the object at runtime.

    Types of Polymorphism in Java

    In Java, polymorphism can be roughly categorized into two prime types: 

    • Compile-time polymorphism (or static polymorphism)
    • Runtime polymorphism (or dynamic polymorphism)

    Compile-time Polymorphism:

    Compile-time polymorphism in Java is achieved with the implementation of method overloading and operator overloading. What does method overloading and operator overloading do? Method overloading and operator overloading grants capabilities for multiple methods in a class and operators to have the same name but with different parameter list (different parameter types, numbers or sequence).

    The compiler used for the code’s compilation then determines the appropriate method to be invoked based on the number, types, and order of the arguments passed during the method call. The decision is made at compile time. This type of polymorphism program in Java is resolved during the compilation process and thus the name "compile-time polymorphism”.

    Compile-time Polymorphism in java Example:

    public class PolymorphismExample {
      public static void main(String[] args) {
      int sum1 = add(5, 3);
      int sum2 = add(5, 3, 2);
     
      System.out.println("Sum 1: " + sum1); // Output: Sum 1: 8
      System.out.println("Sum 2: " + sum2); // Output: Sum 2: 10
      }
     
      public static int add(int a, int b) {
      return a + b;
      }
     
      public static int add(int a, int b, int c) {
      return a + b + c;
      }
     } 

    In this example, we have a class Polymorphism Example with two methods named add. These methods are overloaded, which means they have the same name but different parameter lists.

    The first add method takes two integer parameters a and b and returns their sum. The second add method takes three integer parameters a, b, and c and returns their sum.

    In the main method, we invoke the add method twice with different sets of arguments. The Java compiler determines which add method to call based on the number and types of arguments provided.

    During the compile-time, the compiler matches the method calls with the appropriate method signatures. In this case, it selects the add method with two parameters for the first method call, and the add method with three parameters for the second method call.

    The output will be "Sum 1: 8" and "Sum 2: 10", demonstrating how compile-time polymorphism resolves the method calls based on the static types of the arguments provided at compile-time.

    Runtime Polymorphism:

    Runtime (or dynamic) polymorphism is achieved with the application of method overriding. The decision of which method to invoke is determined at runtime based on the actual data type of the object.

    Polymorphism in Java example programs for runtime polymorphism:

    public class Animal {
     public void sound() {
     System.out.println("Animal makes a sound");
     }
    }
    public class Dog extends Animal {
     @Override
     public void sound() {
     System.out.println("Dog barks");
     }
    }
    public class Cat extends Animal {
     @Override
     public void sound() {
     System.out.println("Cat meows");
     }
    }

    The given code defines a hierarchy of classes: Animal, Dog, and Cat.

    The Animal class has a method called sound(), which prints "Animal makes a sound" when called. The Dog and Cat classes are subclasses of Animal and override the sound() method with their own implementations.

    In the Dog class, the sound() method is overridden to print "Dog barks" when called. Similarly, in the Cat class, the sound() method is overridden to print "Cat meows" when called.

    When the code is executed using a compiler, if we create an instance of the class Animal and call its sound() method, it will print "Animal makes a sound". However, if we alternately create some instances of Dog or Cat and call their sound() methods, they will print "Dog barks" and "Cat meows", respectively.

    The output of the code depends on which class's sound() method is called, resulting in the respective animal sound being printed.

    At runtime, the appropriate `sound()` method is called based on the actual type of the object:

    Animal animal1 = new Dog();
    animal1.sound(); // Output: "Dog barks"
    Animal animal2 = new Cat();
    animal2.sound(); // Output: "Cat meows"

    Runtime polymorphism allows for dynamic binding, where the method implementation is resolved based on the actual type of the object at runtime, rather than the reference type. Get ahead of the race and learn more with Python Programming course online.

    Characteristics of Polymorphism

    Let us explore the prime characteristics of polymorphism:

    • Coercion:

    Coercion, which is also known as implicit type conversion, is an important characteristic of polymorphism, where the language automatically converts one type of data into usable another type when required. In Java, this is commonly seen in use in numeric promotions, where smaller data types are automatically promoted to larger ones to perform arithmetic operations without explicit casting.

    public class NumericPromotionExample {
    public static void main(String[] args) {
    int x = 5;
    double y = 2.5;
    double result = x + y; // int 'x' is promoted to double for addition
    System.out.println("Result: " + result); // Output: Result: 7.5
    }
    }

    In this example, we have made use of an int variable x with a value of 5 and a double variable y with a value of 2.5. When we perform the addition x + y, the int value x is automatically promoted to the usage of a double to match the data type of y. This promotion allows the addition to be performed without any explicit casting.

    The resulting value is stored in the double variable result, which holds the value 7.5. Finally, the value of result is printed, displaying "Result: 7.5" as the output.

    This example clearly demonstrates how using Java automatically promotes for usage of smaller data types, such as int, to larger data types, such as double, when performing arithmetic operations.

    • Internal Operator Overloading:

    Java, however, has no operator overloading to date. But, internal operator overloading refers to the ability of built-in operators to act differently based on the operands' types. To take as an example, the + operator can perform addition for numeric operands and also string concatenation for string operands.

    public class OperatorOverloadingExample {
      public static void main(String[] args) {
      int a = 5;
      int b = 3;
      String str1 = "Hello";
      String str2 = " world!";
     
      int sum = a + b; // Addition of numeric operands
      String concat = str1 + str2; // String concatenation
     
      System.out.println("Sum: " + sum); // Output: Sum: 8
      System.out.println("Concatenation: " + concat); // Output: Concatenation: Hello world!
      }
     } 

    In this example, we have two int variables, a and b, holding the values 5 and 3, respectively. When we use the + operator with these numeric operands (a + b), the operator performs addition, resulting in the sum of 8.

    We also have two String variables, str1 and str2, containing the strings "Hello" and " world!" respectively. When we use the + operator with these string operands (str1 + str2), the operator behaves differently based on the types of the operands. In this case, it performs string concatenation, combining the two strings to create a new string "Hello world!".

    The results of both operations are then printed using System.out.println(), displaying "Sum: 8" and "Concatenation: Hello world!" as the output.

    This example illustrates how the + operator in Java exhibits internal operator overloading by performing addition for numeric operands and string concatenation for string operands.

    • Polymorphic Variables or Parameters:

    Polymorphic variables or also known as parameters refer to the ability to use a variable or any parameter of a superclass or interface type to refer to objects of different subclasses that will inherit from that superclass or also implement that interface. This allows for a much more flexible environment and code reuse, as a single variable or parameter can hold different object types, and the appropriate methods will be invoked based on the actual object at runtime.

    public class PolymorphismExample {
      public static void main(String[] args) {
      Animal animal1 = new Dog();
      Animal animal2 = new Cat();
     
      animal1.sound(); // Output: Dog barks
      animal2.sound(); // Output: Cat meows
      }
     }
     
     class Animal {
      public void sound() {
      System.out.println("Animal makes a sound");
      }
     }
     
     class Dog extends Animal {
      @Override
      public void sound() {
      System.out.println("Dog barks");
      }
     }
     
     class Cat extends Animal {
      @Override
      public void sound() {
      System.out.println("Cat meows");
      }
     }

    In this example, we have a class hierarchy consisting of the Animal class and its two subclasses Dog and Cat. The Animal class has a sound() method that prints "Animal makes a sound".

    In the main() method, we declare two variables of type Animal, animal1 and animal2. However, we initialize animal1 with a Dog object and animal2 with a Cat object. This is possible because of polymorphism, where a superclass variable can refer to objects of its subclasses.

    When we invoke the sound() method on animal1 and animal2, the appropriate sound() method of the actual objects (i.e., Dog and Cat) is called based on the runtime type of the objects. This is known as dynamic method dispatch, where the correct implementation of the method is determined at runtime.

    As a result, the output will be "Dog barks" for animal1.sound() and "Cat meows" for animal2.sound(). This demonstrates how polymorphic variables allow for flexibility and code reuse by enabling a single variable to hold different object types and invoke the appropriate methods based on the actual object at runtime.

    • Subtype Polymorphism:

    Subtype polymorphism, which is also known as Java inheritance polymorphism, is another prime characteristic. It has the capability for the objects of a subclass to be treated as objects of their superclass. This grants the ability to code to work with objects at a higher level of ease, promoting code reuse, modularity, and also flexibility to a greater degree. Method overriding is a key mechanism of subtype polymorphism, allowing subclasses to provide their own implementation of methods defined in the superclass.

    These characteristics properties collectively contribute to the vast power and the versatility of polymorphism in Java, enabling for a much more flexible and modular code design and enhancing the code's ability to handle diverse object types.

    public class PolymorphismExample {
      public static void main(String[] args) {
      Animal animal = new Dog(); // Subclass object treated as a superclass object
      animal.sound(); // Output: Dog barks
      }
     }
     
     class Animal {
      public void sound() {
      System.out.println("Animal makes a sound");
      }
     }
     
     class Dog extends Animal {
      @Override
      public void sound() {
      System.out.println("Dog barks");
      }
     } 

    In this example, we have a superclass Animal and its subclass Dog. The Animal class has a sound() method that prints "Animal makes a sound". The Dog class overrides the sound() method to provide its own implementation, which prints "Dog barks".

    In the main() method, we create an object of the Dog class and assign it to a variable of the superclass type Animal. This is possible because of subtype polymorphism, where an object of a subclass can be treated as an object of its superclass.

    When we invoke the sound() method on the animal object, it dynamically dispatches to the overridden method in the Dog class. This means that even though the reference type is Animal, the actual implementation called is from the Dog class.

    As a result, the output will be "Dog barks". This demonstrates the power of subtype polymorphism, where objects of a subclass can be treated as objects of their superclass, promoting code reuse, modularity, and flexibility. Method overriding, as a key mechanism of subtype polymorphism, allows subclasses to provide their own implementation of methods defined in the superclass.

    Advantages & Disadvantages of Polymorphism in Java

    Advantages of Polymorphism in Java:

    • Code Reusability: Polymorphism heavily promotes the use-case of code reuse by allowing objects of different classes to be treated uniformly. This leads to a more modular and also maintainable code to be written, as common behaviors can be easily defined in a superclass or interface and shared across multiple subclasses.
    • Simplicity and Readability: Polymorphism further enhances code readability by allowing for a more generic programming syntax to be used. By programming to a common interface or superclass, the code becomes much more intuitive and easier to understand by any user, as the focus shifts to the shared behaviors rather than the specific implementations.
    • Code Modularity and Maintainability: Polymorphism also promotes modularity, as the changes made to the superclass or interface can easily propagate to all other implementing classes. This reduces code duplication and the risk of errors, making the codebase easier to maintain, debug or update.
    • Runtime Flexibility: Polymorphism also allows for carrying out dynamic method binding, where the appropriate method is determined at runtime based on the actual type of the object. This provides for much more runtime flexibility and enables multiple different behaviors to be executed based on the specific object being referenced.
    • Flexibility and Extensibility: Polymorphism further provides flexibility and also extensibility manifold by enabling the addition of new classes or subclasses without the modification of existing code. This makes it easier to introduce new functionality or variations without disrupting the overall structure of the Java program.

    Disadvantages of Polymorphism in Java:

    • Performance Overhead: The dynamic binding and method dispatching involved in the polymorphism of Java can introduce to users a slight performance overhead when compared to other static method calls. However, the impact is usually very minimal and not a significant concern in most of the applications.
    • Complexity for Beginners: Polymorphism can be initially posed as challenging for novice programmers who are not very familiar with modern object-oriented concepts. Understanding the relationships between classes and interfaces requires a solid grasp of object-oriented principles.
    • Debugging Difficulties: When dealing with any polymorphic code, it can be more challenging to keep track of the flow of execution of code and identify the exact method implementations being invoked. Debugging can be more complex when different subclasses override the same method with different behaviors. IDEs are of great help in such cases.
    • Limited Access to Subclass-specific Features: When using polymorphism in Java, if not downcasted to appropriate subclass type, the invoking code can only access the methods and members which are defined in the superclass or interface. Subclass-specific features will not be able to directly accessible unless downcasted to the appropriate subclass type.
    • Design Complexity: Proper use of polymorphism techniques requires a very thoughtful design decision, which may include defining appropriate superclass and subclass relationships and identifying common behaviors, abstractions and interfaces. Poor design choices can lead to overly complex or convoluted code structures.

    Real-Life Examples of Polymorphism

    Here are a few real-life examples that involve polymorphism and polymorphism in Java is implemented through the following examples:

    • Shape Hierarchy:

    The easiest polymorphism in Java with real time example is the shape hierarchy program. Consider a software application that deals with various different shapes like circles, rectangles, and triangles. These shapes can be represented using a common superclass or an interface called "Shape." Each shape can have its own implementations in its methods like calculateArea() and draw(). By treating all shapes as instances of the "Shape" superclass or interface, you can perform operations like calculating the total area of a collection of shapes or displaying them on a screen, regardless of their specific type.

    • Media Playback:

    In media players, polymorphism can be observed when playing different types of media files such as audio and video. The media player can have a common "MediaPlayer" interface or superclass with methods like play(), pause(), and stop(). Each specific media type, such as MP3 or MP4, can have its own implementation of these methods. In this way, the media player can handle different file formats seamlessly, as long as they adhere to the common interface or superclass.

    • Banking System:

    In a banking system, polymorphism can be utilized when handling various types of accounts like savings accounts, checking accounts, and investment accounts. These different account types can inherit from a common "Account" superclass or implement a common interface. The banking system can treat all accounts uniformly, allowing operations such as deposits, withdrawals, and interest calculations to be performed on any account object, regardless of its specific type.

    • Employee Management:

    In an office environment involving an employee management system, polymorphism can be applied when dealing with multiple different types of employees to keep track, such as full-time employees, part-time employees, and contractors. These employee types can be modeled using a common superclass or interface called "Employee." The system can treat all employees appropriately, allowing common operations like calculating salaries, managing leave requests, and accessing employee details to be performed on any employee object.

    • Animal Classification:

    In the field of biology, polymorphism can be observed in animal classification. Each animal class or subclass can have its own unique characteristics and behaviors. By utilizing polymorphism, scientific studies and research can be conducted using common methods or interfaces that can be implemented by various animal types, regardless of their specific class or subclass.

    These examples demonstrate how polymorphism in Java real-time examples allows for the flexibility, modularity, and code reusability in real-life applications across various domains.

    Conclusion

    Thus, it can be said that polymorphism is a very powerful and essential concept in Java and object-oriented programming for the modern-day developer environment and also in many workplace environments. It enables many useful features such as code reusability, flexibility, and modularity by allowing multiple objects of different classes to be treated uniformly through common interfaces or also super classes. 

    While the concept of polymorphism may introduce a slight performance overhead in some systems and also require thoughtful design considerations, the advantages that it brings outweigh the disadvantages in most cases. Real-life examples like shape hierarchies, media playback, banking systems, and employee management demonstrate the practical applications and benefits of polymorphism in various domains. Go for Java full course and learn from leading Java experts with industry experience.

    Frequently Asked Questions (FAQs)

    1What is the benefit of polymorphism in Java?

    The benefit of polymorphism in Java is that it immensely promotes the credibility of code reusability, thereby allowing objects of different classes to be treated uniformly through common interfaces or superclasses. This leads to more modular and maintainable code, as common behaviors can be defined once and shared across multiple subclasses.

    2What is the best example for polymorphism in Java?

    One of the prime of polymorphism in Java can be defined as the shape hierarchy example, where different shapes like circles, rectangles, and triangles can be represented using a common superclass or interface called "Shape." This allows for many operations like calculating the total area of a collection of shapes or also displaying them on a screen, regardless of their specific type.

    3Is static polymorphism in Java method overloading?

    No  , this is a concept, and overloading is a feature provided in Java. static polymorphism in Java solely refers to the compile-time polymorphism achieved through method overloading, where multiple different methods in a class have the same name but different parameter lists.

    Profile

    Utkarsh Konwar

    Blog Author

    I'm Utkarsh, a student pursuing the field of Computer Science Engineering alongside a corporate career in Full Stack Development, Creative Content Writing, and as a Marketing Associate, with more than 5 years of experience.

    Share This Article
    Ready to Master the Skills that Drive Your Career?

    Avail your free 1:1 mentorship session.

    Select
    Your Message (Optional)

    Upcoming Programming Batches & Dates

    NameDateFeeKnow more
    Course advisor icon
    Course Advisor
    Whatsapp/Chat icon