Another OOP concept that is important to understand is Polymorphism, or more precisely in Java subtype polymorphism as it is present when we have inheritance/subtype relationships.
To introduce this concept we’ll use slight variations of the Light
and DimmableLight
classes we used in a previous section:
public class Light {
public void turnOn() {
System.out.println("Turned the Light on");
}
public void turnOff() {
System.out.println("Turned the Light off");
}
}
public class DimmableLight extends Light {
private int power;
public DimmableLight(int power) {
this.power = power;
}
@Override
public void turnOn() {
System.out.println("Turned on the dimmable light and setting power to: " + power);
}
@Override
public void turnOff() {
System.out.println("Turned off the dimmable light");
}
public void setPower(int power) {
this.power = power;
}
}
Similar to the previous example, we’ve defined two classes, a DimmableLight
class that extends from a Light
base class. In our DimmableLight
class we’re also overriding both methods from the base class, turnOn
and turnOff
.
What do we mean by Polymorphism in Java?
Polymorphism refers to the possibility of an object reference to refer to objects of different types, or seen from a different perspective, the object can take a different form depending on the type of reference pointing to it.
In Java, we’ve already mentioned that this is achievable via subtyping/inheritance.
So far, in all our previous examples, our object references and the objects we create have always been of the same type, for example:
In the example code run the JavaPolymorphismApp
|
Light normalLight = new Light();
We know this is valid as the normalLight
reference is of type Light
and it is pointing to the Light
object that is being created. Both, the reference and the object are of the same type so Java sees this as a valid assignment.
Now, let’s see what happens if we mismatch the types:
Light dimmableLight = new DimmableLight(25);
dimmableLight.turnOff();
Output:
Turned off the dimmable light
The code above is valid and this is down to the support in Java of subtype polymorphism. Our DimmableLight
object is being referenced by an object reference of type Light
.
It is important to note 3 points here:
-
Keep in mind that in these cases the object that has been created is of the type you provided in the
new
statement, in our case ofDimmableLight
- even though the reference is of a different type (this sometimes causes confusion when learning the language). -
You’ll notice that the
turnOff
method that was executed is actually the one from ourDimmableLight
object as it overrides the one in theLight
class. Hence, the message that is printed out is "Turned off the dimmable light". This is called virtual method invocation, Java calls the method for the object and not for the reference type. -
Now, even though our object is of type
DimmableLight
, because the reference we’re using is of typeLight
, the Java compiler will not let us invoke any methods that are not defined in theLight
class.
To clarify the last point, let’s try invoking the setPower
method:
dimmableLight.setPower(10);
In this case, the Java compiler gives us an error stating that the setPower
method isn’t defined in the Light
class. Basically, our DimmableLight
object has taken the form of a Light
object as this is the reference being used to access it.
Invalid usages
Polymorphism in Java allows us to substitute/use an object reference of a supertype to point/refer to an object of the same type or any of its subtypes, like in the examples above Light → Light
or Light → DimmableLight
.
However, we can’t do the following:
-
We can’t use a subtype to refer to an object of a supertype, for example:
DimmableLight invalidRef = new Light();
-
Nor we can use types that aren’t related to one another, for example:
Light invalidRef = new Airplane();
Reason for this is that the reference type being used has methods/state that aren’t defined in the object they refer to. If the example above were valid, it would mean we could invoke invalidRef.turnOn()
and our Airplane
objects don’t have such method.
Hence why this is called subtype polymorphism as it is only valid from supertype reference to subtype objects.
Is that it?
One of the first feelings with polymorphism is that this might not add a lot of value. Basically, we’re saying that we can have an object and use an object reference of a different type (as long as it’s a supertype), like:
Light light = new DimmableLight(10)
.
When looking into software design principles, polymorphism is a very powerful tool that allows developers to build classes/components that aren’t coupled to specific types. Let’s look at an example below.
We won’t go into too many details in this course, but we wanted to note that as simple as this concept is, it becomes of more importance in software design. |
Example with method invocations
Let’s assume we have the following class that is used to handle the lights we have in our system:
public class LightHandler {
public void turnOn(Light light) {
light.turnOn();
}
public void turnOff(Light light) {
light.turnOff();
}
}
The LightHandler
class has two methods that receive a Light
object as a parameter. If polymorphism weren’t a feature in Java, our LightHandler
would have to define turnOn(…)
/turnOff(…)
methods for each type of light we define in our system, for example, turnOn(Light light)
and turnOn(DimmableLight light)
(and others if we defined more subclasses of Light
).
However, given that we have polymorphism supported in Java, we can use objects from subtypes of Light
and pass them to our LightHandler
methods.
For example:
Light normalLight = new Light();
Light dimmableLight = new DimmableLight(25);
LightHandler handler = new LightHandler();
handler.turnOn(normalLight);
handler.turnOn(dimmableLight);
Output:
Turned the Light on
Turned on the dimmable light and setting power to: 25
Although not the focus in the beginners course, the above example starts showing that we can design classes like LightHandler
that are meant to support multiple implementations of a Light
contract. The LightHandler
doesn’t need to know about all the different subtypes/implementations of Light
to do its work; all it needs to know is that Light
objects have a turnOn
and a turnOff
method.
This relates to 2 concepts, the "Liskov Substitution Principle" and "Design by Contract" which are important concepts in software design not covered in this course. This also relates to Interfaces which we’ll cover in this chapter.
|