Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 8 Next »

The term polymorphism can be defined as “having many forms.” A polymorphic reference is a reference variable that can refer to different types of objects at different points in time. The specific method invoked through a polymorphic reference can change from one invocation to the next.

Polymorphism is a concept in object-oriented programming that allows objects to be treated as instances of their parent class, enabling a single interface to represent different types. The term "polymorphism" is derived from Greek words meaning "many shapes," which aptly describes its ability to take on multiple forms.

Polymorphism

  1. Compile-time (or Static) Polymorphism: This is achieved through method overloading, where multiple methods share the same name but have different parameters. The correct method to invoke is determined at compile-time based on the method signature.

  2. Run-time (or Dynamic) Polymorphism: This is achieved through method overriding, where a subclass provides a specific implementation for a method that is already defined in its superclass. The correct method to invoke is determined at run-time based on the object's actual class.

Polymorphism is a fundamental concept in programming, particularly within the paradigm of object-oriented programming (OOP). It describes the ability of different objects to be accessed through the same interface, allowing for different underlying forms (data types) to be manipulated in a uniform manner.

Conceptual Overview:

  1. Polymorphism comes from Greek words meaning "many forms."

  2. It allows objects of different classes to be treated as objects of a common superclass.

  3. The specific form or class of an object is less important than the fact that it has the interface expected; the details of its actual type can remain hidden.

Types of Polymorphism:

  • Ad-hoc Polymorphism: This is achieved through function overloading or operator overloading.

  • Parametric Polymorphism: This allows a function or a data type to be written generically, so it can handle values identically without depending on their type. This is often used in generic programming.

  • Subtype Polymorphism (or Inclusion Polymorphism): This is the form most commonly referred to as "polymorphism" in OOP. It allows a function to use objects of different types at different times, depending on the type of the object it is referencing at that time.

In Object-Oriented Programming:

  1. Method Overriding: In OOP, polymorphism often involves a superclass and one or more subclasses. A method in a subclass can override a method in the superclass. This means that the version of the method that gets invoked is determined by the object's runtime type.

  2. Interface Implementation: An interface defines a set of methods that can be implemented by any class, from any inheritance tree. A class that implements an interface promises to provide the behavior published by that interface.

Practical Example:

Consider a simple class hierarchy where a superclass Shape has a method draw(). Subclasses like Circle, Square, and Triangle each implement draw() differently. If you have a list of Shape objects, you can iterate over them and call draw() on each one, and the correct version of the method is called for each object, whether it's a Circle, Square, or Triangle.

Real-world Analogy

Imagine a simple video game with different types of characters like warriors, archers, and mages. Each character type has a unique way of attacking: warriors might use swords, archers use bows and arrows, and mages use magic spells. However, from the game's perspective, they are all game characters and can perform an "attack" action.

Here, polymorphism allows you to treat all these different character types as general "game characters" and call the "attack" action on them, without needing to know their specific types. The appropriate attack action (sword slash, arrow shot, or magic spell) will be carried out based on the actual type of character, even though your code simply calls the same "attack" method on a general "game character."

Benefits

  1. Code Reusability: You can write code that works on the superclass type, but it will work with any subclass type, providing they follow the expected contract (like method names and parameters).

  2. Code Maintainability: Polymorphism promotes cleaner, more intuitive, and more maintainable code. You can add new subclasses with little or no modification to existing code that uses the superclass.

  3. Flexibility: Polymorphism allows for code that is more flexible and can handle a range of inputs, reducing the complexity of conditional statements.

  4. Abstraction: It allows for abstract design, where you can define a common interface for a group of related classes.

By understanding and using polymorphism, you can write more flexible, maintainable, and reusable code.

At some point, the commitment is made to execute certain code to carry out a method invocation. This commitment is referred to as binding a method invocation to a method definition. In many situations, the binding of a method invocation to a method definition can occur at compile time. For polymorphic references, however, the decision cannot be made until run time. The method definition that is used is based on the object that is being referred to by the reference variable at that moment. This deferred commitment is called late binding or dynamic binding. It is less efficient than binding at compile time, because the decision must be made during the execution of the program. This overhead is generally acceptable in light of the flexibility that a polymorphic reference provides.

Example

/* 
 * Polymorphism  Late binding 
 */

// Animal class - this is the base class 
 class Animal {	 
  public void animalSound()
  {
	  System.out.println("The animal makes a sound");
  } 
   
  // Regular method
  public void sleep() {
    System.out.println("Zzz");
  }
} //end of Animal Base Class
 
 /* *********************************
  * Derived Classes
  *********************************** */

//Subclass (inherit from Animal)
//Notice how we define the Abstract method animalSound declared in the super class 
class Cat extends Animal {
	public void animalSound() {
	// The body of animalSound() is provided here
	System.out.println("The cat says Meow");
	}
} //end of Cat Class

//Subclass (inherit from Animal)
class Dog extends Animal {
	public void animalSound() {
	// The body of animalSound() is provided here
	System.out.println("The dog says Woof");
	}
} //end of Dog class

//Subclass (inherit from Animal)
class Cow extends Animal {
	public void animalSound() {
	// The body of animalSound() is provided here
	System.out.println("The dog says Moo");
	}
} 


/*
 * ******************************
 * Main Program to demo polymorphism
 * ******************************
 */

public class Demo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		 Animal myAnimal = new Animal();  // Create a Animal object
		 myAnimal.animalSound();
		 myAnimal.sleep();
		 
		 //now create an Animal and make it a Dog
		 Animal myDog = new Dog();
		 myDog.animalSound();
		 myDog.sleep();
		 
		 //now create an Animal and make it a Cat
		 Animal myCat = new Cat();
		 myCat.animalSound();
		 myCat.sleep();
		 
		 Animal myCow = new Cow();
		 myCow.animalSound();
		 myCow.sleep();
		 
		 //demonstration of instanceof
		 if(myCat instanceof Cat)
		 {
			 System.out.println("This is a cat!");
		 }
		 if(myCat instanceof Animal)
		 {
			 System.out.println("This is a Animal!");
		 }

	}

}

The given Java code is a demonstration of polymorphism in Java using animal sounds as examples. Polymorphism allows objects to take on many forms. The most common use of polymorphism in OOP occurs when a parent class reference is used to refer to a child class object.

Let's go through the important parts:

  1. The Animal class is the parent (base) class with two methods: animalSound() and sleep(). animalSound() is overridden in each of the child classes while sleep() is not, meaning it's the same across all animal types.

  2. Cat, Dog, and Cow are subclasses that each extend Animal. They each override the animalSound() method to print out a unique sound for that animal. This is a perfect example of method overriding in Java, which is one of the ways we can achieve polymorphism.

  3. In the main method, we first create a generic Animal object and call its methods. This is standard object-oriented programming.

  4. Then we create Dog, Cat, and Cow objects but refer to them with Animal references. This is polymorphism in action - we can call the animalSound() method on these references and it will call the overridden method in the respective subclass, even though the reference type is Animal. This is known as late binding or dynamic method dispatch.

  5. The sleep() method is also called on these references. Since sleep() is not overridden in the subclasses, the original sleep() method from Animal class is called.

  6. Finally, the instanceof keyword is used to check if myCat is an instance of Cat and Animal. instanceof returns true if the object being compared is an instance of the specified type or an instance of a subclass of the specified type. So, in this case, both checks will return true because myCat is a Cat object and Cat is a subclass of Animal. This is why "This is a cat!" and "This is a Animal!" both get printed.

  • No labels