<< Chapter < Page | Chapter >> Page > |
Polymorphism, on the other hand, is looking at the class hierarchy from the top down. Any subclass can be used anywhere the superclass is needed because the subclasses are all abstractly equivalent to the superclass. Different behaviors may arise because the subclasses may all have different implementations of the abstract behaviors defined in the superclass. For instance, all liquids have a boiling temperature. They may have different values for that boiling temperature which leads to different behaviors at any given temperature.
Polymorphism is arguably the more useful perspective in an object-oriented programming paradigm. Polymorphism describes how an entity of a lower abstraction level can be substituted for an entity of a higher abstraction level and in the process, change the overall behavior of the original system. This will be the cornerstone that enables us to build OO systems that are flexible, extensible, robust and correct.
Let's explore some different ways in which polymorphism presents itself. Consider the following example of the union design pattern:
/**
* An interface that represents an operation on two doubles*/
public interface IBinaryOp {double apply( double x, double y); // all interface methods are public and abstract by default
}/*** An IBinaryOp that adds two numbers
*/public class AddOp implements IBinaryOp {
public double apply( double x, double y) {return x+y;
}}/**
* An IBinaryOp that multiplies two numbers*/
public class MultOp implements IBinaryOp {public double apply( double x, double y) {
return x*y;}public String getDescription() {
return "MultOp is a multiplying function.";}
}
Is the following legal code?
IBinaryOp bop = new IBinaryOp()
;
No, it won't compile.
IBinaryOp
is an interface and does not specify any actual executable code, so it cannot be instantiated.
Is the following legal code?
IBinaryOp bop = new AddOp()
;
Yes!
AddOp
is an concrete class and can be instantiated.
AddOp
is an
IBinaryOp
(technically,
AddOp
implements the
IBinaryOp
interface), so
bop
can reference it.
Given the above declaration and assignment of
bop
, is the following assignment then possible?
bop = new MultOp()
;
Yes, for the same reasons as the previous exercise!
MultOp
is an concrete class and can be instantiated.
MultOp
is an
IBinaryOp
, so
bop
can reference it.
Suppose we have
bop = new AddOp()
; , what is the result of
bop.apply(5,3)
?
The result is 8 because
bop
refers to an
AddOp
instance, whose
apply
method adds its two input values.
Suppose we now say
bop = new MultOp()
, what is the result of
bop.apply(5,3)
now?
The result is 15 because
bop
now refers to an
MultOp
instance, whose
apply
method multiplies its two input values.
Suppose we have some variable, called
myOp
of type
IBinaryOp
what is the result of
myOp.apply(5,3)
?
It is impossible to tell because it depends on the exact type of the object instance to which
myOp
refers.
Suppose we have
bop = new MultOp()
, is it legal to call
bop.getDescription()
?
No, because
bop
is a variable of type
IBinaryOp
, which is not defined as having a
getDescription
method. This is true even if bop references an object of type MultOp. This is because the
static typing of
bop
tells the compiler that it references an
IBinaryOp
, not the particular concrete type of the object it currently references. If we had
MultOp mop = new MultOp()
, then
mop.getDescription()
is perfectly legal.
Is the following legal code?
AddOp aop = new AddOp()
Yes, because
aop
is a variable of type
AddOp
, and thus can reference an instance of the same type.
Given the declaration in the previous exercise, is the following legal?
aop = new MultOp()
No, because
aop
is a variable of type
AddOp
, and
MultIOp
is not an
AddOp
, so
aop
cannot reference an instance of
MultOp
.
Suppose we have definitions of
aop
and
bop
from above. Is the following legal? That is, can we compile and run the folowing statement without error?
bop = aop;
Yes!
bop
is a variable of type
IBinaryOp
, and
aop
is defined as referencing an
AddOp
object, which is an
IBinaryOp
.
Is the converse legal as well? That is, using the above definitions, can we compile and run the following statement?
aop = bop;
Not as written, because
bop
is a variable of type
IBinaryOp
(i.e. staticly typed as such), which and does not necessarily reference an object of type
AddOp
. to which
aop
must reference. That is, a variable of the type of the superclas can always reference an object of the type of any subclass, but a variable of the type of a particular subclass cannot necessarily reference something of the type of its superclass. Another way of saying this is that a superset contains its subsets but not the other way around. The above assignment will cause a compile time error because the compiler cannot know if the assignment is possible. An explicit cast is required to supress the compile time error (
aop = (AddOp) bop
), but may trigger a run-time error if indeed the instance referred to by
bop
is not of type
AddOp
.
Notification Switch
Would you like to follow the 'Principles of object-oriented programming' conversation and receive update notifications?