Java 11 Developer Certification - Polymorphism

January 24, 2021

What we are covering in this lesson

  1. Introduction
  2. Overloading vs Overriding methods
  3. Code Examples
  4. Method Overriding Code Example

Introduction

Polymorphism simply means many forms. In the Object Oriented world, an object can be many things or types. It can be treated as it’s declared type, it’s inherited type, it’s parent’s inherited type, a java.lang.Object, or any of the interfaces it implements.

We did look at polymorphism in the previous section a bit, when we implemented the drive and park method on different types of Vehicles.

Overloading vs Overriding methods

We have already discussed this, but the below table will simplify the differences between them.

Rule Overloaded method Overriden method
Methods must have the same name TRUE TRUE
Methods must have the same number of parameters FALSE TRUE
Methods must have the same types of parameters in the same order. Names do not matter FALSE TRUE
Methods must have the same return type or a covariant of the type. Primitive return types must exactly match Irrelevant in determination TRUE
Methods must have the same access or less restrictive Irrelevant in determination TRUE
Exception handling (throws clause) Irrelevant in determination Not required to have a throws clause, but if included, the exception cannot be wider or less specific

There are not many similarities in between overloaded and overriden methods. The main similarity is that the methods name must be the same. An overridden method will have the same name of a method, defined on a parent class or an interface. An overloaded method can have the same name as a method in its own class, or can overload a method on a parent class or interface.

Overloaded methods have much fewer restrictions, because they are basically new methods in the class they are declared in, that happen to have the same method name as another, but a different parameter list. When you override a method, you’re intentionally, one would hope, replacing or extending method that exists in another class, either because you want to, or because you are forced to. And it is the overridden method’s particular signature, that allows a calling class to be agnostic about the class that is passed to it.

Code Examples

The below example is totally self explanatory. The Motorcycle class extends the abstract Vehicle class, and implements its methods drive, park and makeNoise. The main class creates an instance of Auto, AutomatedAuto and Motocycle types. Each of these is ultimately a Vehicle and can be passed to this class’ drive and park methods.

package maxCode.online.inheritance;

//We create a new Vehicle, Motorcycle, which implements
//the abstract methods on Vehicle
class Motorcycle extends Vehicle {
	public Motorcycle(VehicleType type, String owner, String make) {
		super(type, owner, make);
	}

	public void drive() {
		System.out.println("Kick up stand.  Go");
	}

	public void park() {
		System.out.println("Put down stand.");
	}

	public void makeNoise() {
		System.out.println("Zoom!");
	}
}

// TestVehichles class demonstrates polymorphism
public class TestVehicles {
	public static void main(String[] args) {

		// We create 3 types of 'Vehicle' classes
		Auto car = new Auto(Vehicle.VehicleType.Automobile, "Allen", "Ford");
		AutomatedAuto automatedCar = new AutomatedAuto(Vehicle.VehicleType.Automobile, "Martha", "Tesla");

		Motorcycle harley = new Motorcycle(Vehicle.VehicleType.Motorcycle, "Buzz", "Harley-Davidson");

		// We execute this class's drive method the same regardless of the
		// type of object passed...
		drive(car);
		drive(automatedCar);
		drive(harley);

		// We execute this class's park method the same regardless of the
		// type of object passed...
		park(car);
		park(automatedCar);
		park(harley);

	}

	// We create a generic drive method that makes no assumptions about
	// what the object's drive method will do, it just knows from the
	// abstract class that drive() is a valid method on Vehicle.
	public static void drive(Vehicle v) {
		System.out.print(v.getClass().getName() + " drive() :  ");
		v.drive();
	}

	// We create a generic park method that makes no assumptions about
	// what the object's drive method will do, it just knows from the
	// abstract class that park() is a valid method on Vehicle.
	public static void park(Vehicle v) {
		System.out.print(v.getClass().getName() + " park() :  ");
		v.park();
	}
}

Output

maxCode.online.inheritance.Auto drive() :  Release Brake, go
maxCode.online.inheritance.AutomatedAuto drive() :  Driving it myself
maxCode.online.inheritance.Motorcycle drive() :  Kick up stand.  Go
maxCode.online.inheritance.Auto park() :  Parallel or back in?
maxCode.online.inheritance.AutomatedAuto park() :  Parking it myself
maxCode.online.inheritance.Motorcycle park() :  Put down stand.

The above code demonstrates the following

  • It demonstrates that Vehicle published a public interface that told all consumers, that any Vehicle, or subclass of Vehicle, would have the following behaviour: drive, park and makeNoise.
  • It demonstrates that each unique subclass of Vehicle, could customise its implementation of these methods, overriding the methods, and do its own specific thing.
  • Further, it demonstrates that the print method in TestVehicles can now execute the print method on any type of Vehicle passed into the code.
  • There is no need for the calling code to know anything more about the specifics of the object passed to it.
  • It demonstrates that although we passed a Vehicle to the methods on TestVehicles, the objects passed were different and concrete incarnations of Vehicle.
  • And, finally, it demonstrates that executing the contractual methods drivePark, and makeNoise, on a parameter object of type Vehicle, actually executed the overridden methods of the subclass.

Method Overriding Code Example

An overridden method in ExtendedClass must have the same signature, the same return type, and it’s access modifier must not be more restrictive, something we have seen in the tabular format before.

Lets look at the below code

package maxCode.online.polymorphism;

class BaseClass {
	public void goodMethod() {
		System.out.println("BaseClass executing good method");
	}

	public void printInformation(CharSequence s) {
		System.out.println("BaseClass prints " + s);
	}

	public CharSequence getInformation() {
		return getClass().getName();
	}
}

class ExtendedClass extends BaseClass {
	public void goodMethod() {
		super.goodMethod();
		System.out.println("AND ExtendedClass executing a better method");
	}

	public void printInformation(CharSequence string) {
		System.out.println("ExtendedClass prints " + string);
	}

	public String getInformation() {
		return getClass().getName();
	}
}

public class OverrideExample {
	public static void main(String[] args) {
		ExtendedClass e = new ExtendedClass();
		e.goodMethod();
		e.printInformation(e.getInformation());
		e.printInformation((CharSequence) e.getInformation());
	}
}
BaseClass executing good method
AND ExtendedClass executing a better method
ExtendedClass prints maxCode.online.polymorphism.ExtendedClass
ExtendedClass prints maxCode.online.polymorphism.ExtendedClass

You can extend the functionality of a base class’s method in the extended class instead of completely bypassing it, by calling the parent method using super. The base class method executes first and then the child class.

You can use subtypes in the return types of overridden methods, as shown in this example, but not in the parameter lists. Parameter names are not important, it can be different. Only the parameter types must be same.

We cannot modify the access modifier for printInformation in child class to private, otherwise it will give a compiler error.

The overridden method can eliminate the throws clause completely or it can declare a throws clause with an exception type that’s the same or a more specific file type. Trying to throw a less specific exception or one that’s not considered a covariant of the original method isn’t allowed.

So if you don’t override correctly, you could get a compiler error, or no error, because your method is a valid overloaded method instead.

Lets have a look at another code example with a slightly different scenario.

package maxCode.online.polymorphism;

import java.util.Arrays;

//MostBasicClass has 3 overloaded methods, all named baseMethod
class MostBasicClass {
	public void baseMethod() {
		System.out.println("- PARENT OVERLOAD 'void baseMethod()'");
		return;
	}

	public Object baseMethod(String s) {
		System.out.println("- PARENT OVERLOAD " + "'Object baseMethod(String s)'");
		return s;
	}

	public int baseMethod(int... intArray) throws ArrayIndexOutOfBoundsException {
		System.out.println("- PARENT OVERLOAD " + "'int baseMethod(int... intArray)'");
		return intArray[intArray.length + 1];
	}
}

// OverrideOverload class extends MostBasicClass
public class OverrideOverload extends MostBasicClass {
	// This method overrides one of MostBasicClass's overloaded methods
	public void baseMethod() {
		super.baseMethod();
		System.out.println("- CHILD OVERRODE 'void baseMethod()'");
	}

	// Overload baseMethod in the child class..
	public Object baseMethod(String[] s) {
		System.out.println("- CHILD OVERLOADED " + " 'Object baseMethod(String[] s)'");
		return Arrays.toString(s);
	}

	// Override baseMethod in the child class..
	// Note that it is ok to define a return type which can be said to be an Object.
	public Integer baseMethod(String s) {
		System.out.println("- CHILD OVERRODE " + " 'Object baseMethod(String s)' with " + "'Integer baseMethod(String s)'");
		return Integer.valueOf(s);
	}

	public int baseMethod(int[] intArray) {
		System.out.println("- CHILD OVERRODE 'int baseMethod(int[] intArray)'");
		return intArray[intArray.length - 1];
	}

	// Main method will call our overloaded, overridden methods
	public static void main(String[] args) {
		int[] intArray = new int[] { 1, 2, 3, 4, 5 };
		OverrideOverload oo = new OverrideOverload();
		int i = 0;
		oo.baseMethod();
		oo.baseMethod("10");
		oo.baseMethod(new String[] { "10" });

		try {
			// We'll make the call with var args
			i = oo.baseMethod(intArray);

		} catch (Exception e) {
			System.out.println("Uh oh, error occurred in call to" + " oo.baseMethod(intArray)");
		}
		System.out.println("local variable i = " + i);
	}
}
- PARENT OVERLOAD 'void baseMethod()'
- CHILD OVERRODE 'void baseMethod()'
- CHILD OVERRODE  'Object baseMethod(String s)' with 'Integer baseMethod(String s)'
- CHILD OVERLOADED  'Object baseMethod(String[] s)'
- CHILD OVERRODE 'int baseMethod(int[] intArray)'
local variable i = 5

So this code consists of a class called MostBasicClass. And it’s got three overloaded methods, as you can see there, baseMethod.

We have also got a class called OverrideOverload that extends the MostBasicClass and overrides one of the overloaded methods there.

It has also got a main method where we’re going to invoke the classes and then actually call some of these methods.

Here we have tried to overload baseMethod in the child class, signature and return type must match.

If we throw Exception in baseMethod overloaded version in child class, the compiler throws error. You’re allowed to override a variable arg’s method with an array reference but you will have to call it with an array parameter, if you do that.

You can’t override an instance method in a parent with a static method in the child. And the reverse is also true, you cannot override a static method on the parent with an instance method on the child. So if add static to baseMethod in one of parent or child methods, the compiler will complain.

So thats all for this section, see you in the next one!