ITI 1121. Introduction to Computing II – subtitle
ITI 1121. Introduction to Computing II
Inheritance: polymorphism
by
Marcel Turcotte
Version February 8, 2020
Preamble
Preamble
Overview
Overview
Inheritance: polymorphism
The concept of inheritance in Java promotes code reuse and supports the notion of
polymorphism.
General objective:
This week you will be able to create polymorphic methods.
1 69
Preamble
Learning objectives
Learning objectives
Describe the concept of polymorphism.
Create polymorphic methods.
Compare the interface and the abstract class.
Lectures:
Pages 7–31, 39–45 of E. Koffman and P. Wolfgang.
2 69
Preamble
Plan
Plan
1 Preamble
2 Polymorphism
3 Inheritance and Java
4 Prologue
3 69
Polymorphism
Polymorphism
From the Greek polus = several and morphê = forms, so it means which has
several forms.
4 69
Definitions
In computer science, polymorphism consists in allowing the use of an identifier for
different entities (see different types).
1. Polymorphism ad hoc (name overloading): the same method name is associated
with different blocks of code. These methods have the same name, but they differ by
their list of parameters.
2. Subtype polymorphism (by inheritance): an identifier is linked to data of different
types by a subtype relationship.
3. Parametric polymorphism (generic): the class has one or more formal type
parameters.
5 69
Overloading
The PrintStream class uses ad hoc polymorphism to implement the println method.
p r i n t l n ( )
p r i n t l n ( boolean v a l u e )
p r i n t l n ( char v a l u e )
p r i n t l n ( char [ ] v a l u e )
p r i n t l n ( double v a l u e )
p r i n t l n ( f l o a t v a l u e )
p r i n t l n ( i n t v a l u e )
p r i n t l n ( long v a l u e )
6 69
Name overloading (continued)
Three methods having different signatures ∗.
pub l i c s t a t i c i n t sum( i n t a , i n t b , i n t c ) {
re tu rn a + b + c ;
}
pub l i c s t a t i c i n t sum( i n t a , i n t b ) {
re tu rn a + b ;
}
pub l i c s t a t i c double sum( double a , double b ) {
re tu rn a + b ;
}
∗In Java, the signature of a method includes the method name and the parameter list, but not the
return value.
7 69
Polymorphism by subtype
Problem : implement a method isLeftOf which returns true if this shape is located to
the left of its argument (another geometric shape) and false otherwise.
8 69
isLeftOf
C i r c l e c1 , c2 ;
c1 = new C i r c l e ( 1 0 . 0 , 20 . 0 , 5 . 0 ) ;
c2 = new C i r c l e ( 2 0 . 0 , 10 . 0 , 5 . 0 ) ;
i f ( c1 . i s L e f tO f ( c2 ) ) {
System . out . p r i n t l n ( ” c1 i s L e f tO f c2 ” ) ;
} e l s e {
System . out . p r i n t l n ( ” c2 i s L e f tO f c1 ” ) ;
}
9 69
isLeftOf
Rec tang l e r1 , r2 ;
r1 = new Rec tang l e ( 0 . 0 , 0 . 0 , 1 . 0 , 1 . 0 ) ;
r2 = new Rec tang l e ( 100 . 0 , 100 .0 , 200 .0 , 4 0 0 . 0 ) ;
i f ( r1 . i s L e f tO f ( r2 ) ) {
System . out . p r i n t l n ( ” r1 i s L e f tO f r2 ” ) ;
} e l s e {
System . out . p r i n t l n ( ” r2 i s L e f tO f r1 ” ) ;
}
10 69
isLeftOf
i f ( r1 . i s L e f tO f ( c1 ) ) {
System . out . p r i n t l n ( ” r1 i s L e f tO f c1 ” ) ;
} e l s e {
System . out . p r i n t l n ( ” c1 i s L e f tO f r1 ” ) ;
}
i f ( c2 . i s L e f tO f ( r2 ) ) {
System . out . p r i n t l n ( ” c2 i s L e f tO f r2 ” ) ;
} e l s e {
System . out . p r i n t l n ( ” r2 i s L e f tO f c2 ” ) ;
}
11 69
An outrageous solution!
pub l i c boolean i s L e f tO f ( C i r c l e c ) {
re tu rn getX ( ) < c . getX ( ) ;
}
pub l i c boolean i s L e f tO f ( Rec tang l e r ) {
re tu rn getX ( ) < r . getX ( ) ;
}
Why?
12 69
An outrageous solution!
pub l i c boolean i s L e f tO f ( C i r c l e c ) {
re tu rn getX ( ) < c . getX ( ) ;
}
pub l i c boolean i s L e f tO f ( Rec tang l e r ) {
re tu rn getX ( ) < r . getX ( ) ;
}
As many implementations as there are varieties of shapes!
Yet, all the implementations are identical!
Whenever a new category of shape is defined (say Triangle), a new method isLeftOf
must be created!
13 69
Solution
Suggestions?
pub l i c boolean i s L e f tO f ( "Any Shape " s ) {
re tu rn getX ( ) < s . getX ( ) ;
}
How to write any “Any Shape” in Java?
14 69
Solution
Let’s implement the isLeftOf method in the Shape class as follows.
pub l i c boolean i s L e f tO f ( Shape s ) {
re tu rn getX ( ) < s . getX ( ) ;
}
15 69
isLeftOf
C i r c l e c ;
c = new C i r c l e ( 1 0 . 0 , 20 . 0 , 5 . 0 ) ;
Rec tang l e r ;
r = new Rec tang l e ( 0 . 0 , 0 . 0 , 1 . 0 , 1 . 0 ) ;
i f ( c . i s L e f tO f ( r ) ) {
System . out . p r i n t l n ( " c i s L e f tO f r " ) ;
} e l s e {
System . out . p r i n t l n ( " r i s L e f tO f c " ) ;
}
16 69
isLeftOf
i f ( c . i s L e f tO f ( r ) ) {
// . . .
The method isLeftOf of the object designated by the reference c is called.
Perfect, c designates an object of the class Circle, the latter inherits the method
isLeftOf.
17 69
isLeftOf
i f ( c . i s L e f tO f ( r ) ) {
// . . .
Um, during the call, the value of the actual parameter, r, is copied to the formal
parameter, s.
Should we conclude that the following statements are also valid?
Shape s ;
Rec tang l e r ;
r = new Rec tang l e ( 0 . 0 , 0 . 0 , 1 . 0 , 1 . 0 ) ;
s = r ;
18 69
Types
“A variable is a storage location and has an associated type, sometimes called its
compile-time type, that is either a primitive type (§4.2) or a reference type (§4.3).
A variable always contains a value that is assignment compatible (§5.2) with its
type.”
“Assignment of a value of compile-time reference type S (source) to a variable of
compile-time reference type T (target) is checked as follows:
If S is a class type:
If T is a class type, then S must either be the same class as T, or S must be a subclass
of T, or a compile-time error occurs.”
⇒ Gosling et al. (2000) The Java Language Specification.
19 69
isLeftOf
Indeed, this definition confirms that the following statements are valid.
Shape s ;
Rec tang l e r ;
r = new Rec tang l e ( 0 . 0 , 0 . 0 , 1 . 0 , 1 . 0 ) ;
s = r ;
but not “r = s”!
20 69
Polymorphism
A variable s designates an object of the class Shape or one of its subclasses.
Shape s ;
Utilisation:
s = new C i r c l e ( 0 . 0 , 0 . 0 , 1 . 0 ) ;
s = new Rec tang l e ( 1 0 . 0 , 100 .0 , 10 . 0 , 1 0 0 . 0 ) ;
21 69
Polymorphism
pub l i c boolean i s L e f tO f ( Shape o th e r ) {
boolean r e s u l t ;
i f ( getX ( ) < o the r . getX ( ) ) {
r e s u l t = t rue ;
} e l s e {
r e s u l t = f a l s e ;
}
re tu rn r e s u l t ;
}
Usage:
C i r c l e c = new C i r c l e ( 1 0 . 0 , 10 . 0 , 5 . 0 ) ;
Rec tang l e d = new Rec tang l e ( 0 . 0 , 10 . 0 , 12 . 0 , 2 4 . 0 ) ;
i f ( c . i s L e f tO f ( d ) ) { . . . }
22 69
Exercises
Shape s ;
C i r c l e c ;
c = new C i r c l e ( 0 . 0 , 0 . 0 , 1 . 0 ) ;
s = c ;
i f ( c . getX ( ) ) { . . . } // v a l i d ?
i f ( s . getX ( ) ) { . . . } // v a l i d ?
i f ( c . g e tRad iu s ( ) ) { . . . } // v a l i d ?
i f ( s . g e tRad iu s ( ) ) { . . . } // v a l i d ?
23 69
Remarks
Shape s ;
C i r c l e c ;
c = new C i r c l e ( 0 . 0 , 0 . 0 , 1 . 0 ) ;
s = c ;
The object designated by s remains a circle (Circle). The class of an object remains
the same throughout the execution of the program.
24 69
Remarks
Shape s ;
C i r c l e c ;
c = new C i r c l e ( 0 . 0 , 0 . 0 , 1 . 0 ) ;
s = c ;
i f ( s . getX ( ) ) { . . . }
When we use s to designate a circle (Circle), the object “is seen as” a geometrical
shape (Shape), in the sense that we only see the characteristics (methods and
variables) defined in the class Shape.
25 69
Remarks
Polymorphism is a powerful concept. The method isLeftOf that we have defined can
be used not only to handle circles and rectangles, but also any object of a future
subclass of the class Shape.
pub l i c c l a s s Tr i a n g l e extends Shape {
// . . .
}
26 69
Calculating the area
Problem : We want all geometric shapes (objects in the subclasses of Shape) to have
a method for calculating the area.
27 69
What do you mean, Marcel?
pub l i c c l a s s Shape {
// . . .
pub l i c i n t compareTo ( Shape o th e r ) {
i f ( a r ea ( ) < o the r . a r ea ( ) ) {
re tu rn −1;
} e l s e i f ( a r ea ( ) == othe r . a r ea ( ) ) {
re tu rn 0 ;
} e l s e {
re tu rn 1 ;
}
}
}
28 69
What do you think?
pub l i c c l a s s Shape {
// . . .
// Must be r e d e f i n e d by the s u b c l a s s e s o r e l s e . . .
pub l i c double a r ea ( ) {
re tu rn −1.0;
}
pub l i c i n t compareTo ( Shape o th e r ) {
i f ( a r ea ( ) < o the r . a r ea ( ) ) {
re tu rn −1;
} e l s e i f ( a r ea ( ) == othe r . a r ea ( ) ) {
re tu rn 0 ;
} e l s e {
re tu rn 1 ;
}
}
}
29 69
Abstract
pub l i c c l a s s Shape {
// . . .
pub l i c abs t rac t double a r ea ( ) ;
pub l i c i n t compareTo ( Shape o th e r ) {
i f ( a r ea ( ) < o the r . a r ea ( ) ) {
re tu rn −1;
} e l s e i f ( a r ea ( ) == othe r . a r ea ( ) ) {
re tu rn 0 ;
} e l s e {
re tu rn 1 ;
}
}
}
30 69
Abstract
pub l i c abs t rac t c l a s s Shape {
// . . .
pub l i c abs t rac t double a r ea ( ) ;
pub l i c i n t compareTo ( Shape o th e r ) {
i f ( a r ea ( ) < o the r . a r ea ( ) ) {
re tu rn −1;
} e l s e i f ( a r ea ( ) == othe r . a r ea ( ) ) {
re tu rn 0 ;
} e l s e {
re tu rn 1 ;
}
}
}
31 69
Abstract classes
A class declaring an abstract method must be abstract.
You can’t create objects of an abstract class.
A class can be declared abstract, even if it does not contain abstract methods.
32 69
What have we achieved?
pub l i c c l a s s C i r c l e extends Shape {
}
Circle.java:1: Circle is not abstract and
does not override abstract method area() in Shape
public class Circle extends Shape {
^
1 error
33 69
pub l i c c l a s s C i r c l e extends Shape {
p r i v a t e double r a d i u s ;
pub l i c C i r c l e ( double r a d i u s ) {
t h i s . r a d i u s = r a d i u s ;
}
pub l i c double ge tRad iu s ( ) {
re tu rn r a d i u s ;
}
pub l i c double a r ea ( ) {
re tu rn Math . PI ∗ r a d i u s ∗ r a d i u s ;
}
pub l i c vo id s c a l e ( double f a c t o r ) {
r a d i u s ∗= f a c t o r ;
}
}
Name lookup
+ getId() : long
Account
+ getBalance() : double
+ getMonthlyFees() : double
BankAccount
CheckingAccount
+ getMonthlyFees() : double
SavingAccount
BankAccount and SavingAccount both have a method named getMonthlyFees.
35 69
BankAccount:
pub l i c double getMonth lyFees ( ) {
re tu rn 2 5 . 0 ;
}
SavingAccount:
pub l i c double getMonth lyFees ( ) {
double r e s u l t ;
i f ( ge tBa l ance ( ) > 5000 .0 ) {
r e s u l t = 0 . 0 ;
} e l s e {
r e s u l t = super . ge tMonth lyFees ( ) ;
}
re tu rn r e s u l t ;
}
Consider the following statements:
Account a ;
BankAccount b ;
Sav ingAccount s ;
s = new Sav ingAccount ( ) ;
s . ge tMonth lyFees ( ) ;
b = s ;
b . getMonth lyFees ( ) ;
a = b ;
a . getMonth lyFees ( ) ;
Dynamic binding
Let S (source) be the type of the object currently designated by a reference variable
of type T (target).
Unless the method is static or final, the lookup
1. occurs at runtime, and
2. starts at the class S:
if the method is found, this is the method that will be executed,
otherwise the immediate superclass is considered,
this process continues until the first occurrence of the method is found.
⇒ A.K.A. late binding or virtual binding
38 69
Inheritance and Java
Object
In Java, classes are organized in a tree structure. The most general class, the one at
the root of the tree, is called Object.
Object
#clone(): Object
+equals(Object:obj): boolean
+getClass(): Class
+toString(): String
Number
+byteValue(): byte
+doubleValue(): double
+floatValue(): float
+intValue(): int
+longValue(): long
+shortValue()
Integer
+MAX_VALUE: int
+MIN_VALUE: int
+byteValue(): byte
+doubleValue(): double
+floatValue(): float
+intValue(): int
+longValue(): long
+shortValue()
+compareTo(i:integer): int
+parseInt(s:String): int
+toString(): String
Double
+MAX_VALUE: double
+MIN_VALUE: double
+byteValue(): byte
+doubleValue(): double
+floatValue(): float
+intValue(): int
+longValue(): long
+shortValue()
+compareTo(d:Double): int
+parseDouble(s:String): double
+toString(): String
39 69
Object
If the superclass is not explicitly mentioned, Object is the default superclass, so the
following statement:
pub l i c c l a s s C {
}
is equivalent to this one:
pub l i c c l a s s C extends Object {
}
40 69
equals
The class Object defines a method equals.
Every Java object therefore has a method equals.
So we can always write a.equals(b) if a and b are reference variables.
Object
#clone(): Object
+equals(Object:obj): boolean
+getClass(): Class
+toString(): String
Number
+byteValue(): byte
+doubleValue(): double
+floatValue(): float
+intValue(): int
+longValue(): long
+shortValue()
Integer
+MAX_VALUE: int
+MIN_VALUE: int
+byteValue(): byte
+doubleValue(): double
+floatValue(): float
+intValue(): int
+longValue(): long
+shortValue()
+compareTo(i:integer): int
+parseInt(s:String): int
+toString(): String
Double
+MAX_VALUE: double
+MIN_VALUE: double
+byteValue(): byte
+doubleValue(): double
+floatValue(): float
+intValue(): int
+longValue(): long
+shortValue()
+compareTo(d:Double): int
+parseDouble(s:String): double
+toString(): String
41 69
equals
This is the equals method of the Object class.
pub l i c boolean e qua l s ( Object ob j ) {
re tu rn ( t h i s == ob j ) ;
}
42 69
Account
pub l i c c l a s s Account {
p r i v a t e i n t i d ;
p r i v a t e S t r i n g name ;
pub l i c Account ( i n t id , S t r i n g name) {
t h i s . i d = i d ;
t h i s . name = name ;
}
}
43 69
Test
pub l i c c l a s s Test {
pub l i c s t a t i c vo id main ( S t r i n g [ ] a r g s ) {
Account a , b ;
a = new Account (1 , new S t r i n g ( “Marce l ” ) ) ;
b = new Account (1 , new S t r i n g ( “Marce l ” ) ) ;
i f ( a . e qu a l s ( b ) ) {
System . out . p r i n t l n ( ” a and b a r e e qua l s ” ) ;
} e l s e {
System . out . p r i n t l n ( ” a and b a r e not e qua l s ” ) ;
}
}
}
What will the result be?
44 69
pub l i c c l a s s Account {
p r i v a t e i n t i d ;
p r i v a t e S t r i n g name ;
pub l i c Account ( i n t id , S t r i n g name) {
t h i s . i d = i d ;
t h i s . name = name ;
}
pub l i c boolean e qua l s ( Object o ) {
boolean r e s u l t = t rue ;
i f ( o == nu l l ) { // <−−−
r e s u l t = f a l s e ;
} . . .
re tu rn r e s u l t ;
}
}
pub l i c c l a s s Account {
p r i v a t e i n t i d ;
p r i v a t e S t r i n g name ;
pub l i c Account ( i n t id , S t r i n g name) {
t h i s . i d = i d ;
t h i s . name = name ;
}
pub l i c boolean e qua l s ( Object o ) {
boolean r e s u l t = t rue ;
i f ( o == nu l l ) {
r e s u l t = f a l s e ;
} e l s e i f ( t h i s . g e tC l a s s ( ) != o . g e tC l a s s ( ) ) { // <−−−
r e s u l t = f a l s e ;
} . . .
re tu rn r e s u l t ;
}
}
pub l i c c l a s s Account {
p r i v a t e i n t i d ;
p r i v a t e S t r i n g name ;
pub l i c Account ( i n t id , S t r i n g name) { . . . }
pub l i c boolean e qua l s ( Object o ) {
boolean r e s u l t = t rue ;
i f ( o == nu l l ) {
r e s u l t = f a l s e ;
} e l s e i f ( t h i s . g e tC l a s s ( ) != o . g e tC l a s s ( ) ) {
r e s u l t = f a l s e ;
} e l s e {
Account o th e r = ( Account ) o ; // <−−−
. . .
}
re tu rn r e s u l t ;
}
}
pub l i c c l a s s Account {
p r i v a t e i n t i d ; p r i v a t e S t r i n g name ;
pub l i c Account ( i n t id , S t r i n g name) { . . . }
pub l i c boolean e qua l s ( Object o ) {
boolean r e s u l t = t rue ;
i f ( o == nu l l ) {
r e s u l t = f a l s e ;
} e l s e i f ( t h i s . g e tC l a s s ( ) != o . g e tC l a s s ( ) ) {
r e s u l t = f a l s e ;
} e l s e {
Account o th e r = ( Account ) o ;
i f ( i d != o th e r . i d ) {
r e s u l t = f a l s e ;
} e l s e i f ( name == nu l l && othe r . name != nu l l ) {
r e s u l t = f a l s e ;
} e l s e i f ( name != nu l l && ! name . e qua l s ( o t h e r . name) ) {
r e s u l t = f a l s e ;
}
}
re tu rn r e s u l t ;
}
}
Test
pub l i c c l a s s Test {
pub l i c s t a t i c vo id main ( S t r i n g [ ] a r g s ) {
Account a , b ;
a = new Account (1 , new S t r i n g ( "Marce l " ) ) ;
b = new Account (1 , new S t r i n g ( "Marce l " ) ) ;
i f ( a . e qu a l s ( b ) ) {
System . out . p r i n t l n ( " a and b a r e e qua l s " ) ;
} e l s e {
System . out . p r i n t l n ( " a and b a r e not e qua l s " ) ;
}
}
}
What will the result be?
49 69
toString()
Since the class Object declares a method toString(), all objects have this method.
Either the class inherits a method toString() or it redefines it.
Thus, the statement a.toString() is always valid if a is a reference variable.
50 69
toString()
Account a ;
a = new Account (101 , "Marce l " ) ;
System . out . p r i n t l n ( a ) ;
System . out . p r i n t l n ( a . t o S t r i n g ( ) ) ;
51 69
System.out.println
pub l i c c l a s s Pr in tS t r eam {
// . . .
pub l i c vo id p r i n t l n ( Object ob j ) {
w r i t e ( S t r i n g . va lueOf ( ob j ) ) ;
}
}
pub l i c c l a s s S t r i n g {
// . . .
pub l i c s t a t i c S t r i n g va lueOf ( Object ob j ) {
re tu rn ( ob j == nu l l ) ? " n u l l " : ob j . t o S t r i n g ( ) ;
}
}
52 69
pub l i c c l a s s Account {
p r i v a t e i n t i d ;
p r i v a t e S t r i n g name ;
pub l i c Account ( i n t id , S t r i n g name) { . . . }
// . . .
}
toString()
Account a ;
a = new Account (101 , "Marce l " ) ;
System . out . p r i n t l n ( a ) ;
> java Test
Account@3fee733d
54 69
toString()
Since the class Object declares a method toString(), all objects have this method.
Either the class inherits a method toString() or it redefines it.
Thus, the statement a.toString() is always valid if a is a reference variable.
pub l i c c l a s s Object {
// . . .
pub l i c S t r i n g t o S t r i n g ( ) {
re tu rn g e tC l a s s ( ) . getName()+”@”+I n t e g e r . t oHexS t r i ng ( hashCode ( ) ) ;
}
}
55 69
pub l i c c l a s s Account {
p r i v a t e i n t i d ;
p r i v a t e S t r i n g name ;
pub l i c Account ( i n t id , S t r i n g name) { . . . }
// . . .
pub l i c S t r i n g t o S t r i n g ( ) {
re tu rn ” Account : i d = ” + i d + ” , name = ” + name ;
}
}
toString()
Account a ;
a = new Account (101 , “Marce l ” ) ;
System . out . p r i n t l n ( a ) ;
> java Test
Account: id = 101, name = Marcel
57 69
Example
import j a v a . awt . Te x tF i e l d ;
pub l i c c l a s s TimeF ie ld extends Tex tF i e l d {
pub l i c Time getTime ( ) {
re tu rn Time . parseTime ( getText ( ) ) ;
}
}
// java.lang.Object
// |
// +–java.awt.Component
// |
// +–java.awt.TextComponent
// |
// +–java.awt.TextField
// |
// +–TimeField
58 69
instanceof
Occasionally, one wants to determine whether a (polymorphic) variable designates an
object of a given class or one of its subclasses.
We then use the operator instanceof or the instance method isInstance.
If, on the other hand, one wants to know if a (polymorphic) variable designates an
object of a certain class, but not one of its subclasses, then use this.getClass() ==
other.getClass().
59 69
pub l i c c l a s s Test {
pub l i c s t a t i c vo id main ( S t r i n g [ ] a r g s ) {
Shape [ ] shapes = new Shape [ 5 ] ;
Shape s = new C i r c l e ( 100 . 0 , 200 .0 , 1 0 . 0 ) ;
shapes [ 0 ] = s ;
shapes [ 1 ] = nu l l ;
shapes [ 2 ] = new Rec tang l e ( 5 0 . 0 , 50 . 0 , 10 . 0 , 1 5 . 0 ) ;
shapes [ 3 ] = new C i r c l e ( ) ;
shapes [ 4 ] = new Rec tang l e ( ) ;
i n t count = 0 ;
f o r ( Shape shape : shapes ) {
i f ( shape i n s t anceo f C i r c l e ) {
count++;
}
}
System . out . p r i n t l n ( ” There a r e ” + count + ” c i r c l e s ” ) ;
}
}
pub l i c c l a s s Test {
pub l i c s t a t i c vo id main ( S t r i n g [ ] a r g s ) {
Shape [ ] shapes = new Shape [ 5 ] ;
Shape s = new C i r c l e ( 100 . 0 , 200 .0 , 1 0 . 0 ) ;
shapes [ 0 ] = s ;
shapes [ 1 ] = nu l l ;
shapes [ 2 ] = new Rec tang l e ( 5 0 . 0 , 50 . 0 , 10 . 0 , 1 5 . 0 ) ;
shapes [ 3 ] = new C i r c l e ( ) ;
shapes [ 4 ] = new Rec tang l e ( ) ;
i n t count = 0 ;
f o r ( Shape shape : shapes ) {
i f ( shape != nu l l && shape . i s I n s t a n c e o f ( s ) ) {
count++;
}
}
System . out . p r i n t l n ( ” There a r e ” + count + ” c i r c l e s ” ) ;
}
}
Implementation to be avoided!
On the next page, the example uses getClass().getName().equals(“Circle”).
This solution offers no type safety.
If I make a typo in the class name for the parameter to the method equals, it is still a
well-formed string, it will be compiled, but the program will not work as expected.
With the first two approaches, this error is detected at compile time.
Later, if I change the class name (“refactor”) to Cercle (French for “circle”), with the
first two approaches, the compiler will find all cases where I use “ref instanceof
Circle”, but not getClass().getName().equals(“Circle”).
62 69
pub l i c c l a s s Test {
pub l i c s t a t i c vo id main ( S t r i n g [ ] a r g s ) {
Shape [ ] shapes = new Shape [ 5 ] ;
Shape s = new C i r c l e ( 100 . 0 , 200 .0 , 1 0 . 0 ) ;
shapes [ 0 ] = s ;
shapes [ 1 ] = nu l l ;
shapes [ 2 ] = new Rec tang l e ( 5 0 . 0 , 50 . 0 , 10 . 0 , 1 5 . 0 ) ;
shapes [ 3 ] = new C i r c l e ( ) ;
shapes [ 4 ] = new Rec tang l e ( ) ;
i n t count = 0 ;
f o r ( Shape shape : shapes ) {
i f ( shape . g e tC l a s s ( ) . getName . e qua l s ( ” C i r c l e ” ) ) {
count++;
}
}
System . out . p r i n t l n ( ” There a r e ” + count + ” c i r c l e s ” ) ;
}
}
getClass()
The contract of the method equals requires that the method be symmetrical. That
is, a.equals(b) and b.equals(a) gives the same result.
If instanceof were used, this property might not be verified in the context of a class
hierarchy where the method equals is redefined in a subclass.
It is therefore preferable to use this.getClass() == other.getClass(), as shown on
the next page.
https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/
lang/Object.html
64 69
https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Object.html
https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Object.html
pub l i c c l a s s Account {
p r i v a t e i n t i d ; p r i v a t e S t r i n g name ;
pub l i c Account ( i n t id , S t r i n g name) { . . . }
pub l i c boolean e qua l s ( Object o ) {
boolean r e s u l t = t rue ;
i f ( o == nu l l ) {
r e s u l t = f a l s e ;
} e l s e i f ( t h i s . g e tC l a s s ( ) != o . g e tC l a s s ( ) ) {
r e s u l t = f a l s e ;
} e l s e {
Account o th e r = ( Account ) o ;
i f ( i d != o th e r . i d ) {
r e s u l t = f a l s e ;
} e l s e i f ( name == nu l l && othe r . name != nu l l ) {
r e s u l t = f a l s e ;
} e l s e i f ( name != nu l l && ! name . e qua l s ( o t h e r . name) ) {
r e s u l t = f a l s e ;
}
}
re tu rn r e s u l t ;
}
}
Prologue
Summary
Inheritance allows for the creation of polymorphic methods.
A reference variable of type T can be used to store the reference of objects from the
class T or any of its subclasses.
When a superclass declares an abstract method, it forces the subclasses to provide
an implementation for the method.
A class that declares an abstract method must be abstract.
One cannot create an object from an abstract class.
Object is the most general class in Java.
All the classes inherit directly or indirectly from the class Object.
Object declares the methods equals, toString, getClass, etc.
All objects in Java have a method equals and toString.
Subclasses can override methods.
The name lookup mechanism always starts with the class of the object, not the
compile time type of the reference variable (unless the method is static or final).
Called dynamic or late binding.
66 69
Next module
Generics
67 69
References I
E. B. Koffman and Wolfgang P. A. T.
Data Structures: Abstraction and Design Using Java.
John Wiley & Sons, 3e edition, 2016.
68 69
Marcel Turcotte
Marcel.
School of Electrical Engineering and Computer Science (EECS)
University of Ottawa
69 / 69
Marcel.
Preamble
Overview
Learning objectives
Plan
Polymorphism
Inheritance and Java
Prologue