S.J.S. tutorial 15: Basic inheritance and polymorphism
SECTION(15, Tutorial 15)
SUBSECTION(15.1,Basic Inheritance)
m5_question(When you see the following code: TT(CLASS CLSS(X) EXTENDS
CLSS(Y)), it means that class CLSS(X) EM(inherits) from the class
CLSS(Y). Class CLSS(X) is called the EM(subclass) and the class
CLSS(Y) is called the EM(superclass) or sometimes the EM(parent)
class. When the class CLSS(X) extends from CLSS(Y), it pulls in all
of the non-private methods and properties from the superclass CLSS(Y).
Inherited methods can overide the behaviour of that same method in the
superclass to give behaviour that is specific to the subclass. The
concept of methods overriding other methods is called EM(dynamic
method binding) or more commonly the more impressive-sounding name:
EM(polymorphism). The main thing that this tutorial shows is the idea
that inheritance is a non-symmetrical relationship. For example: in
the code that follows, the CLSS(Bird) class inherits from the
CLSS(Animal) class, which corresponds to the idea that EM(every bird
is an animal). The reverse, EM(every animal is a bird) is plainly not
true! Inheritance forces you to recognise this.)
m5_question(BO(Question 15.1:) Study, compile and run the
following code. The following code shows how inheritance works. In
the following code, the CLSS(Bird) class inherits from the CLSS(Animal)
class. The CLSS(Bird) class pulls in the CLSS(Animal) class's TT(age)
property and the TT(canFly) and TT(talk) methods. Importantly the
TT(canFly) property overides the behaviour of the TT(canFly) method of
the parent CLSS(Animal) class, which reflects that fact that generally
speaking, birds can fly. In the code that follows, note that EM(int)
properties are initialised to zero by default and the EM(super) method
(also known as the constructor of the superclass) is called by default
if there is a zero parameter constructor in the superclass, which
there is by default, even if you don't write one!
m4_begin_indent
NU()CLASS CLSS(Animal) EOL
BEGIN EOL
EOL
PD PROPERTY INT VARI(age); COMM(// Animal's age in years) EOL
PD PROPERTY INT VARI(health); COMM(// Animal's health in hit points) EOL
EOL
PD CONSTRUCTOR CLSS(Animal)() EOL
PD BEGIN EOL
PD PD age = NUMB(0); COMM(// NOTE: not needed as set by default) EOL
PD PD health = NUMB(100); EOL
PD END EOL
EOL
PD METHOD BOOLEAN FUNC(canFly)() EOL
PD BEGIN EOL
PD PD RETURN FALSE; EOL
PD END EOL
EOL
PD METHOD VOID FUNC(talk)() EOL
PD BEGIN EOL
PD PD SYSTEM_OUT_PRINTLN(STRI("Hello")); EOL
PD END EOL
END EOL
EOL
CLASS CLSS(Bird) EXTENDS CLSS(Animal) EOL
BEGIN EOL
EOL
PD PROPERTY DOUBLE VARI(flySpeed); COMM(// Bird's speed in km/h) EOL
EOL
PD CONSTRUCTOR CLSS(Bird)() EOL
PD BEGIN EOL
PD PD SUPER();PD PADD()PD COMM(// NOTE: not needed as called by default) EOL
PD PD flySpeed = NUMB(0); COMM(// NOTE: not needed as set by default) EOL
PD END EOL
EOL
PD METHOD BOOLEAN FUNC(canFly)() EOL
PD BEGIN EOL
PD PD RETURN TRUE; EOL
PD END EOL
EOL
PD METHOD VOID FUNC(peck)() EOL
PD BEGIN EOL
PD PD SYSTEM_OUT_PRINTLN(STRI("peck")); EOL
PD END EOL
END EOL
EOL
CLASS CLSS(InheriTest) EOL
BEGIN EOL
PD BEGIN_MAIN EOL
PD PD VAR CLSS(Bird) VARI(eagle) = NEW CLSS(Bird)(); EOL
PD PD eagle.talk(); EOL
PD PD eagle.peck(); EOL
PD END_MAIN EOL
END EOL
m4_end_indent
)
m5_question(BO(Question 15.2:) Override the TT(talk) method of the
CLSS(Animal) class in the CLSS(Bird) class to print out STRI(TT("Tweet
Tweet!")) rather than STRI(TT("hello")) to give more accurate talking
of bird objects.)
m5_question(BO(Question 15.3:) By copying the pattern established
in the CLSS(Bird) class, change the eagle from an instance of the Bird
class to its own class in its own right and then create an instance of
that class in the MAIN function of CLSS(InheriTest). Your CLSS(Eagle)
class should have one property: TT(int numberOfKills) and one method:
TT(void attack()) that internally increments the value of
TT(numberOfKills). In the MAIN function you should call every method
of the Eagle class and its superclasses.)
m5_question(BO(Question 15.4:) What is the advantage of using a
new separate class to represent a new object rather than using an
instance of an existing class?)
m5_question(BO(Question 15.5:) Create a new class CLSS(Kiwi) that
inherits from the CLSS(Bird) class. Your CLSS(Kiwi) class should override
the TT(canFly) method to return false, which reflects the fact that
generally speaking birds can fly, but the kiwi bird in particular does
not fly. Your CLSS(Kiwi) class have a property TT(numberOfWorms). Once
you have written the CLSS(Kiwi) class you should create an instance of
the CLSS(Kiwi) class in the MAIN function.)
m5_question(BO(Question 15.6:) Why does the following line of code
in the MAIN function print out 100 but there is no setting of that
variable to that value in the Kiwi class?
m4_begin_indent
NU()SYSTEM_OUT_PRINTLN(k.health); EOL
m4_end_indent
)
m5_question(BO(Question 15.7:) In the classes CLSS(Animal),
CLSS(Bird), CLSS(Eagle) and CLSS(Kiwi), remove all of the TT(canFly) methods
and replace it with a single TT(canFly) property of the CLSS(Animal)
class. In the constructors you will need to set the value of the
TT(canFly) property to a value that is appropriate for that class.
For example in the CLSS(Bird) class's constructor you should set the
TT(canFly) property to true, while in the CLSS(Kiwi) class's constructor
you should set the TT(canFly) property to false.)
m5_question(BO(Question 15.8:) What is the advantage of having a
TT(canFly) property over a bunch of TT(canFly) methods?)
m5_question(There is an equally valid alternative to having a public
property in the CLSS(Animal) class and that is to have in the CLSS(Animal)
class a private property TT(canFly) and a pair of methods for getting
and setting the value of the TT(canFly) property like so. These
methods in S.J.S. and Java are called EM(getter) methods and EM(setter)
methods since, as their names suggest, getters are used for getting
the value of something and setters are used for setting the value of
something. Nore that the TT(canFly) method of the code above
corresponds to TT(getCanFly) method in the code below.
m4_begin_indent
NU()PRIVATE PROPERTY BOOLEAN VARI(canFly); EOL
EOL
METHOD BOOLEAN FUNC(getCanFly)() EOL
BEGIN EOL
PD RETURN canFly; EOL
END EOL
EOL
METHOD VOID FUNC(setCanFly)(BOOLEAN VARI(aCanFly)) EOL
BEGIN EOL
PD canFly = aCanFly; EOL
END EOL
m4_end_indent
NU()You might think that it is simpler to have one thing (a single
non-private property) rather than three things (a private property and
a non-private getter method and a non-private setter method) and you
would be right. However from the point of view of the client code
that uses the CLSS(Animal) class, the two approaches are identical.
Later on when you learn more you will understand under what
circumstances the second getter and setter approach is better.
)
m5_question(BO(Question 15.9:) Change the MAIN function to what
follows:
m4_begin_indent
NU()VAR CLSS(Bird) VARI(b) = NEW CLSS(Bird)(NUMB(10)); EOL
VAR CLSS(Animal) VARI(a) = b; EOL
a.talk(); EOL
a.peck(); EOL
m4_end_indent
NU()When you compile this code it gives a compilation error. What line
gives the error and what is the reason for the error?)
m5_question(BO(Question 15.10:) Change the MAIN function to what
follows:
m4_begin_indent
NU()VAR CLSS(Animal) VARI(a) = NEW CLSS(Animal)(); EOL
VAR CLSS(Bird) VARI(b) = a; EOL
b.talk(); EOL
b.peck(); EOL
m4_end_indent
NU()When you compile this code it gives a compilation error. What line
gives the error and what is the reason for the error?
)
SUBSECTION(15.2,Run-time type enquiry)
m5_question(In S.J.S. and Java there is a keyword called EM(instanceof)
that does a run-time check on the type of an object. The following
function:
m4_begin_indent
NU()FUNCTION VOID FUNC(say)(CLSS(Animal) VARI(a)) EOL
BEGIN EOL
PD SYSTEM_OUT_PRINTLN(a INSTANCEOF CLSS(Bird)); EOL
END EOL
m4_end_indent
NU()uses the INSTANCEOF keyword to determine the run-time type of the
reference TT(a) and prints out whether or not the reference is
referring to a CLSS(Bird) object. Some examples should clarify the
situation:
m4_begin_itemize
m4_item TT(say(NEW CLSS(Bird)())) prints TRUE, Since the parameter TT(a) is pointing to a bird object at run-time,
m4_item TT(say(NEW CLSS(Animal)())) prints FALSE since not every animal is a bird,
m4_item TT(say(NEW CLSS(Eagle)())) prints TRUE, since every eagle is a bird, and
m4_item TT(say(NEW CLSS(Kiwi)())) prints TRUE, since every kiwi is a bird.
m4_item TT(VAR CLSS(Animal) VARI(a) = NEW CLSS(Animal)(); say(a);) prints FALSE since at run-time TT(a) is not pointing to a bird object
m4_item TT(VAR CLSS(Animal) VARI(a) = NEW CLSS(Bird)(); say(a);) prints TRUE since at run-time TT(a) is pointing to a bird object.
m4_end_itemize
In TUTE_BADIF you will learn why in most cases it is better to use
polymorphism instead of the INSTANCEOF keyword for run-time type
enquiry.
SUBSECTION(15.3, The superclass of all objects)
Every class in Java inherits either directly or indirectly from a
class called EM(Object). That is to say if TT(x) is a reference
variable, then the run-time expression TT(x instanceof CLSS(Object))
is always true except for the pathological case where TT(x) is NULL
(i.e. is currently pointing to EM(no object)). The TT(Object) class
contains a method called TT(toString) that returns a string containing
the run-time class name of the object concatenated with the memory
address of the object in base 16 (also known as EM(hexadecimal))
format. Since every class inherits from CLSS(Object), every object
can have TT(toString) invoked upon it. Even better, every class
CLSS(X) can override TT(toString) to provide debugging information
that is tailored to CLSS(X). Therefore the TT(toString) method is
convenient for debugging. Since the TT(toString) method is a public
method of the CLSS(Object) class it must be overidden as a public
method, since your overriden function cannot have weaker access
privileges.)