Inheritance Readings: OOSCS2 Chapters 14 – 16
EECS3311 A: Software Design Fall 2019
CHEN-WEI WANG
Aspects of Inheritance
Code Reuse
Substitutability
X Polymorphism and Dynamic Binding
X Sub-contracting
[ compile-time type checks ] [ runtime behaviour checks ]
2 of 62
Why Inheritance: A Motivating Example
Problem: A student management system stores data about students. There are two kinds of university students: resident students and non-resident students. Both kinds of students have a name and a list of registered courses. Both kinds of students are restricted to register for no more than 30 courses. When calculating the tuition for a student, a base amount is first determined from the list of courses they are currently registered (each course has an associated fee). For a non-resident student, there is a discount rate applied to the base amount to waive the fee for on-campus accommodation. For a resident student, there is a premium rate applied to the base amount to account for the fee for on-campus accommodation and meals. Tasks: Design classes that satisfy the above problem statement. At runtime, each type of student must be able to
register a course and calculate their tuition fee.
3 of 62
The COURSE Class
class
COURSE
create — Declare commands that can be used as constructors make
feature — Attributes title: STRING
fee: REAL
feature — Commands
make (t: STRING; f: REAL)
— Initialize a course with title ’t’ and fee ’f’.
do
title := t
fee := f end
end
4 of 62
No Inheritance: RESIDENT STUDENT Class
RESIDENT STUDENT
class
create make
feature — Attributes
name: STRING
courses: LINKED_LIST[COURSE]
premium rate: REAL
feature — Constructor make (n: STRING)
do name := n ; create courses.make end feature — Commands
set pr (r: REAL) do premium rate := r end
register (c: COURSE) do courses.extend (c) end feature — Queries
tuition: REAL local base: REAL do base := 0.0
across courses as c loop base := base + c.item.fee end
Result := base * premium rate
end end
5 of 62
No Inheritance: NON RESIDENT STUDENT Class
NON RESIDENT STUDENT
class
create make
feature — Attributes
name: STRING
courses: LINKED_LIST[COURSE]
discount rate: REAL
feature — Constructor make (n: STRING)
do name := n ; create courses.make end feature — Commands
set dr (r: REAL) do discount rate := r end
register (c: COURSE) do courses.extend (c) end feature — Queries
tuition: REAL local base: REAL do base := 0.0
across courses as c loop base := base + c.item.fee end
Result := base * discount rate end
end
6 of 62
No Inheritance: Testing Student Classes
test_students: BOOLEAN local
c1, c2: COURSE
jim: RESIDENT_STUDENT jeremy: NON_RESIDENT_STUDENT
do
create c1.make (“EECS2030”, 500.0) create c2.make (“EECS3311”, 500.0) create jim.make (“J. Davis”) jim.set_pr (1.25)
jim.register (c1)
jim.register (c2)
Result := jim.tuition = 1250
check Result end
create jeremy.make (“J. Gibbons”) jeremy.set_dr (0.75) jeremy.register (c1) jeremy.register (c2)
Result := jeremy.tuition = 750
end
7 of 62
No Inheritance:
Issues with the Student Classes
Implementations for the two student classes seem to work. But can you see any potential problems with it?
The code of the two student classes share a lot in common.
Duplicates of code make it hard to maintain your software!
This means that when there is a change of policy on the common part, we need modify more than one places.
This violates the Single Choice Principle :
when a change is needed, there should be a single place (or a minimal number of places) where you need to make that change.
8 of 62
No Inheritance: Maintainability of Code (1)
What if a new way for course registration is to be implemented? e.g.,
register(Course c) do
if courses.count >= MAX_CAPACITY then — Error: maximum capacity reached.
else
courses.extend (c) end
end
We need to change the register commands in both student classes!
Violation of the Single Choice Principle 9 of 62
No Inheritance: Maintainability of Code (2)
What if a new way for base tuition calculation is to be implemented?
e.g.,
tuition: REAL
local base: REAL
do base := 0.0
across courses as c loop base := base + c.item.fee end Result := base * inflation rate * …
end
We need to change the tuition query in both student classes.
Violation of the Single Choice Principle 10 of 62
No Inheritance:
A Collection of Various Kinds of Students
How do you define a class StudentManagementSystem that contains a list of resident and non-resident students?
class STUDENT_MANAGEMENT_SYSETM
rs : LINKED_LIST[RESIDENT STUDENT]
nrs : LINKED_LIST[NON RESIDENT STUDENT]
add_rs (rs: RESIDENT STUDENT) do … end
add_nrs (nrs: NON RESIDENT STUDENT) do … end register_all (Course c) — Register a common course ’c’.
do
across rs as c loop c.item.register (c) end across nrs as c loop c.item.register (c) end
end end
But what if we later on introduce more kinds of students?
Inconvenient to handle each list of students, in pretty much the
same manner, separately! 11 of 62
Inheritance Architecture
inherit
STUDENT
inherit
RESIDENT STUDENT NON RESIDENT STUDENT
12 of 62
Inheritance: The STUDENT Parent Class
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
STUDENT
class
create make
feature — Attributes
name: STRING
courses: LINKED_LIST[COURSE]
feature — Commands that can be used as constructors.
make (n: STRING) do name := n ; create courses.make end feature — Commands
register (c: COURSE) do courses.extend (c) end feature — Queries
tuition: REAL local base: REAL do base := 0.0
across courses as c loop base := base + c.item.fee end
Result := base end
end
13 of 62
Inheritance:
The RESIDENT STUDENT Child Class
1 2 3 4 5 6 7 8 9
10 11
12 13 14 15
class
RESIDENT_STUDENT
inherit
STUDENT
redefine tuition end
create make
feature — Attributes
premium rate : REAL feature — Commands
set pr (r: REAL) do premium_rate := r end feature — Queries
tuition: REAL
local base: REAL
do base := Precursor ; Result := base * premium rate end
end
14 of 62
L3: RESIDENT STUDENT inherits all features from STUDENT.
There is no need to repeat the register command
L14: Precursor returns the value from query tuition in STUDENT.
Inheritance:
The NON RESIDENT STUDENT Child Class
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
class
NON_RESIDENT_STUDENT
inherit
STUDENT
redefine tuition end
create make
feature — Attributes
discount rate : REAL feature — Commands
set dr (r: REAL) do discount_rate := r end feature — Queries
tuition: REAL
local base: REAL
do base := Precursor ; Result := base * discount rate end
end
L3: NON RESIDENT STUDENT inherits all features from STUDENT. There is no need to repeat the register command
L14: Precursor returns the value from query tuition in STUDENT.
15 of 62
Inheritance Architecture Revisited
inherit
STUDENT
inherit
RESIDENT STUDENT NON RESIDENT STUDENT
The class that defines the common features (attributes, commands, queries) is called the parent , super , or
ancestor class.
Each “specialized” class is called a child , sub , or
descendent class. 16 of 62
Using Inheritance for Code Reuse
Inheritance in Eiffel (or any OOP language) allows you to:
X Factor out common features (attributes, commands, queries) in a
separate class.
e.g., the STUDENT class
X Define an “specialized” version of the class which:
inherits definitions of all attributes, commands, and queries e.g., attributes name, courses
e.g., command register
e.g., query on base amount in tuition
This means code reuse and elimination of code duplicates!
defines new features if necessary
e.g., set pr for RESIDENT STUDENT
e.g., set dr for NON RESIDENT STUDENT
redefines features if necessary
e.g., compounded tuition for RESIDENT STUDENT e.g., discounted tuition for NON RESIDENT STUDENT
17 of 62
Testing the Two Student Sub-Classes
test_students: BOOLEAN local
c1, c2: COURSE
jim: RESIDENT_STUDENT ; jeremy: NON_RESIDENT_STUDENT do
create c1.make (“EECS2030”, 500.0); create c2.make (“EECS3311”, 500.0) create jim.make (“J. Davis”)
jim.set_pr (1.25) ; jim.register (c1); jim.register (c2)
Result := jim.tuition = 1250
check Result end
create jeremy.make (“J. Gibbons”)
jeremy.set_dr (0.75); jeremy.register (c1); jeremy.register (c2) Result := jeremy.tuition = 750
end
The software can be used in exactly the same way as before (because we did not modify feature signatures).
But now the internal structure of code has been made
maintainable using . 18 of 62
inheritance
Static Type vs. Dynamic Type
In
X
object orientation , an entity has two kinds of types:
static type is declared at compile time [ unchangeable ]
An entity’s ST determines what features may be called upon it.
dynamic type is changeable at runtime In Java:
In Eiffel:
Student s = new Student(“Alan”);
Student rs = new ResidentStudent(“Mark”);
local s: STUDENT rs: STUDENT
do create {STUDENT} s.make (“Alan”)
create {RESIDENT STUDENT} rs.make (“Mark”)
19 of 62
X
X In Eiffel, the dynamic type can be omitted if it is meant to be the same as the static type:
local s: STUDENT
do create s.make (“Alan”)
Inheritance Architecture Revisited
register (Course c)
tuition: REAL
name: STRING
courses: LINKED_LIST[COUNRSE]
/* new features */
discount_rate: REAL set_dr (r: REAL)
/* redefined features */ tuition: REAL
STUDENT
/* new features */
premium_rate: REAL set_pr (r: REAL)
/* redefined features */ tuition: REAL
RESIDENT_STUDENT
NON_RESIDENT_STUDENT
s1,s2,s3: STUDENT ; rs: RESIDENT STUDENT ; nrs : NON RESIDENT STUDENT create {STUDENT} s1.make (“S1”)
create {RESIDENT STUDENT} s2.make (“S2”)
create {NON RESIDENT STUDENT} s3.make (“S3”)
create {RESIDENT STUDENT} rs.make (“RS”) create {NON RESIDENT STUDENT} nrs.make (“NRS”)
name
courses
reg
tuition
pr
set pr
dr
set dr
s1.
s2.
s3.
rs.
nrs.
20 of 62
Polymorphism: Intuition (1)
1 2 3 4 5 6 7 8 9
local
s: STUDENT
rs: RESIDENT_STUDENT do
create s.make (“Stella”) create rs.make (“Rachael”) rs.set_pr (1.25)
s := rs /* Is this valid? */ rs := s /* Is this valid? */
Which one of L8 and L9 is valid? Which one is invalid?
X L8: What kind of address can s store? [ STUDENT ]
The context object s is expected to be used as: s.register(eecs3311) and s.tuition
X L9: What kind of address can rs store? [ RESIDENT STUDENT ] The context object rs is expected to be used as:
rs.register(eecs3311) and rs.tuition
rs.set pr (1.50) [increase premium rate]
21 of 62
Polymorphism: Intuition (2)
1 2 3 4 5 6
local s: STUDENT ; rs: RESIDENT_STUDENT do create {STUDENT} s.make (“Stella”)
create {RESIDENT_STUDENT} rs.make (“Rachael”) rs.set_pr (1.25)
s := rs /* Is this valid? */
rs := s /* Is this valid? */
rs := s (L6) should be invalid:
s:STUDENT
rs:RESIDENT_STUDENT
STUDENT
name “Stella”
courses
RESIDENT_STUDENT
…
…
rs declared of type RESIDENT STUDENT
calling rs.set pr(1.50) can be expected.
rs is now pointing to a STUDENT object.
Then, what would happen to rs.set pr(1.50)?
CRASH ì rs.premium rate is undefined!!
name courses premium_rate
“Rachael”
1.25
22 of 62
Polymorphism: Intuition (3)
1 2 3 4 5 6
local s: STUDENT ; rs: RESIDENT_STUDENT do create {STUDENT} s.make (“Stella”)
create {RESIDENT_STUDENT} rs.make (“Rachael”) rs.set_pr (1.25)
s := rs /* Is this valid? */
rs := s /* Is this valid? */
s := rs (L5) should be valid:
s:STUDENT
rs:RESIDENT_STUDENT
STUDENT
name “Stella”
courses
RESIDENT_STUDENT
…
…
Since s is declared of type STUDENT, a subsequent call s.set pr(1.50) is never expected.
s is now pointing to a RESIDENT STUDENT object. Then, what would happen to s.tuition?
name courses premium_rate
“Rachael”
1.25
23 of 62
OK ì s.premium rate is just never used!!
Dynamic Binding: Intuition (1)
1 2 3 4 5 6 7 8
local c : COURSE ; s : STUDENT
do crate c.make (“EECS3311”, 100.0)
create {RESIDENT STUDENT} rs.make(“Rachael”) create {NON RESIDENT STUDENT} nrs.make(“Nancy”) rs.set_pr(1.25); rs.register(c) nrs.set_dr(0.75); nrs.register(c)
; check .tuition = 125.0 end ; check .tuition = 75.0 end
s := rs;
s := nrs;
s
s
After s := rs (L7), s points to a RESIDENT STUDENT object. Calling .tuition applies the premium rate.
24 of 62
s
rs:RESIDENT_STUDENT
s:STUDENT
nrs:NON_RESIDENT_STUDENT
RESIDENT_STUDENT
name courses
“Rachael”
premium_rate 1.25
NON_RESIDENT_STUDENT name “Nancy”
courses
discount_rate 0.75
COURSE title “EECS3311”
fee 100.0
Dynamic Binding: Intuition (2)
1 2 3 4 5 6 7 8
local c : COURSE ; s : STUDENT
do crate c.make (“EECS3311”, 100.0)
create {RESIDENT STUDENT} rs.make(“Rachael”) create {NON RESIDENT STUDENT} nrs.make(“Nancy”) rs.set_pr(1.25); rs.register(c) nrs.set_dr(0.75); nrs.register(c)
; check .tuition = 125.0 end ; check .tuition = 75.0 end
s := rs;
s := nrs;
s
s
After s:=nrs (L8), s points to a NON RESIDENT STUDENT object. Calling .tuition applies the discount rate.
25 of 62
s
rs:RESIDENT_STUDENT
s:STUDENT
nrs:NON_RESIDENT_STUDENT
RESIDENT_STUDENT
name courses
“Rachael”
premium_rate 1.25
NON_RESIDENT_STUDENT name “Nancy”
courses
discount_rate 0.75
COURSE title “EECS3311”
fee 100.0
DbC: Contract View of Supplier
Any potential client who is interested in learning about the kind of services provided by a supplier can look through the
(without showing any implementation details):
contract view
class ACCOUNT create
make
feature — Attributes owner : STRING
balance : INTEGER feature — Constructors
make(nn: STRING; nb: INTEGER) require — precondition
positive balance: nb > 0
end feature — Commands
withdraw(amount: INTEGER) require — precondition
non negative amount: amount > 0
affordable amount: amount <= balance -- problematic, why? ensure -- postcondition
balance deducted: balance = old balance - amount
end
invariant -- class invariant
26 of 62
positive balance: balance > 0
end
ES TEST: Expecting to Fail Postcondition (1) model
tests
feature Test Commands for Contract Violations test_withdraw_postcondition_violation
local
acc: BAD_ACCOUNT_WITHDRAW
do
create acc.make (“Alan”, 100)
Violation of Postcondition
with tag “balance_deduced” expected acc.withdraw (50)
end
ACCOUNT
feature Commands
withdraw (amount: INTEGER)
require
non_negative_amount: amount > 0
affordable_amount: amount ≤ balance do
balance := balance amount
ensure
balance_deduced: balance = old balance amount
end
BAD_ACCOUNT_WITHDRAW
feature Redefined Commands withdraw (amount: INTEGER) ++
do
Precursor (amount)
Wrong Implementation balance := balance + 2 * amount
end
TEST_ACCOUNT
acc
27 of 62
ES TEST: Expecting to Fail Postcondition (2.1)
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
class
BAD_ACCOUNT_WITHDRAW
inherit
ACCOUNT
redefine withdraw end
create
make
feature — redefined commands withdraw(amount: INTEGER)
do
Precursor(amount)
— Wrong implementation
balance := balance + 2 * amount
end end
X L3–5: BAD ACCOUNT WITHDRAW.withdraw inherits postcondition from ACCOUNT.withdraw: balance = old balance – amount.
X L11 calls correct implementation from parent class ACCOUNT.
X L13 makes overall implementation incorrect. 28 of 62
ES TEST: Expecting to Fail Postcondition (2.2)
1 class TEST_ACCOUNT
2 inherit ES TEST
3 create make
4 feature — Constructor for adding tests 5 make
6 do
7
8
9 end
add violation case with tag (“balance_deducted”, agent test_withdraw_postcondition_violation)
10 feature — Test commands (test to fail) 11 test_withdraw_postcondition_violation 12 local
13
14 do
acc: BAD_ACCOUNT_WITHDRAW
15
16
17
18
19 end 20 end
comment (“test: expected postcondition violation of withdraw”) create acc.make (“Alan”, 100)
— Postcondition Violation with tag “balance_deduced” to occur. acc.withdraw (50)
29 of 62
Exercise
Recall from the “Writing Complete Postconditions” lecture:
class BANK
deposit_on_v5 (n: STRING; a: INTEGER)
do … — Put Correct Implementation Here. ensure
…
others unchanged :
across old accounts.deep twin as cursor all cursor.item.owner / n implies
cursor.item account_of (cursor.item.owner) end
end end
How do you create a “bad” descendant of BANK that violates this postcondition?
class BAD_BANK_DEPOSIT
inherit BANK redefine deposit end feature — redefined feature
deposit_on_v5 (n: STRING; a: INTEGER) do Precursor (n, a)
accounts[accounts.lower].deposit(a)
end end
30 of 62
Multi-Level Inheritance Architecture (1)
31 of 62
DOMESTIC_STUDENT
STUDENT
FOREIGN_STUDENT
DOMESTIC_RESIDENT_STUDENT
DOMESTIC_NON_RESIDENT_STUDENT
FOREIGN_RESIDENT_STUDENT
FOREIGN_NON_RESIDENT_STUDENT
Multi-Level Inheritance Architecture (2)
SMART_PHONE
dial — basic feature surf_web — basic feature
IOS
surf_web — redefined using safari facetime — new feature
quick_take
zoomage
surf_web — redefined using firefox skype — new feature
side_sync
ANDROID
IPHONE_XS_MAX
IPHONE_11_PRO
HUAWEI
SAMSUNG
32 of 62
HUAWEI_P30_PRO
HUAWEI_MATE_20_PRO
GALAXY_S10
GALAXY_S10_PLUS
Inheritance Forms a Type Hierarchy
A (data) type denotes a set of related runtime values.
X Every class can be used as a type: the set of runtime objects.
Use of inheritance creates a hierarchy of classes: X (Implicit) Root of the hierarchy is ANY.
X Each inherit declaration corresponds to an upward arrow.
X The inherit relationship is transitive: when A inherits B and B
inherits C, we say A indirectly inherits C.
e.g., Every class implicitly inherits the ANY class. Ancestor vs. Descendant classes:
X The ancestor classes of a class A are: A itself and all classes that A directly, or indirectly, inherits.
A inherits all features from its ancestor classes.
A’s instances have a wider range of expected usages (i.e., attributes, queries, commands) than instances of its ancestor classes.
X The descendant classes of a class A are: A itself and all classes that directly, or indirectly, inherits A.
Code defined in A is inherited to all its descendant classes.
33 of 62
Inheritance Accumulates Code for Reuse
The lower a class is in the type hierarchy, the more code it accumulates from its ancestor classes:
X A descendant class inherits all code from its ancestor classes. X A descendant class may also:
Declare new attributes.
Define new queries or commands.
Redefine inheritedqueriesorcommands. Consequently:
X When being used as context objects ,
instances of a class’ descendant classes have a wider range of expected usages (i.e., attributes, commands, queries).
X When expecting an object of a particular class, we may substitute it with an object of any of its descendant classes.
X e.g., When expecting a STUDENT object, substitute it with either a RESIDENT STUDENT or a NON RESIDENT STUDENT object.
X Justification: A descendant class contains at least as many
features as defined in its ancestor classes (but not vice versa!). 34 of 62
Substitutions via Assignments
By declaring , reference variable v1 will store the address of an object of class C1 at runtime.
By declaring , reference variable v2 will store the address of an object of class C2 at runtime.
Assignment copies the address stored in v2 into v1. X v1 will instead point to wherever v2 is pointing to. [ object alias ]
v1:C1
v2:C2
v1:=v2
v1
v2
In such assignment , we say that we an object of type C1 with an object of type C2.
Substitutions are subject to rules! 35 of 62
…
C1
…
C2
…
…
v1:=v2
substitute
Rules of Substitution
Given an inheritance hierarchy:
1. When expecting an object of class A, it is safe to substitute it
with an object of any descendant class of A (including A). X e.g., When expecting an IOS phone, you can substitute it with
either an IPHONE XS MAX or IPHONE 11 PRO.
X ì Each descendant class of A is guaranteed to contain all code
of (non-private) attributes, commands, and queries defined in A. X All features defined in A are guaranteed to be available in the
new substitute.
2. When expecting an object of class A, it is unsafe to substitute
it with an object of any .
X e.g., When expecting an IOS phone, you cannot substitute it with
just a SMART PHONE, because the facetime feature is not
supported in an ANDROID phone.
X ì Class A may have defined new features that do not exist in any
of its parent’s ancestor classes . 36 of 62
ancestor class of A’s parent
Reference Variable: Static Type
A reference variable’s static type is what we declare it to be. X e.g., declares jim’s static type as STUDENT.
X e.g.,
declares a variable my phone of static type SmartPhone.
X The static type of a reference variable never changes.
jim:STUDENT
my phone:SMART PHONE
For a reference variable v, its static type
defines the
A feature call v.m(. . . ) is compilable if m is defined in .
X e.g., After declaring , we may call register and tuition on jim
may not call set pr (specific to a resident student) or set dr
(specific to a non-resident student) on jim
X e.g., After declaring , we
C
expected usages of v as a context object
.
C
jim:STUDENT
my phone:SMART PHONE
37 of 62
may call dial and surf web on my phone
may not call facetime (specific to an IOS phone) or skype (specific to an Android phone) on my phone
Reference Variable: Dynamic Type
A reference variable’s dynamic type is the type of object that it is currently pointing to at runtime.
X The dynamic type of a reference variable may change whenever we re-assign that variable to a different object.
X There are two ways to re-assigning a reference variable.
38 of 62
Reference Variable: Changing Dynamic Type (1)
Re-assigning a reference variable to a newly-created object:
X Substitution Principle : the new object’s class must be a descendant class of the reference variable’s static type.
X e.g., Given the declaration :
changes the dynamic type of jim to RESIDENT STUDENT.
changes the dynamic type of jim to NON RESIDENT STUDENT.
X e.g., Given an alternative declaration :
e.g., is illegal
because STUDENT is not a descendant class of the static type of jim (i.e., RESIDENT STUDENT).
jim:STUDENT
create {RESIDENT STUDENT} jim.make(“Jim”)
create {NON RESIDENT STUDENT} jim.make(“Jim”)
jim:RESIDENT STUDENT
create {STUDENT} jim.make(“Jim”)
39 of 62
Reference Variable: Changing Dynamic Type (2)
Re-assigning a reference variable v to an existing object that is referenced by another variable other (i.e., v := other):
X Substitution Principle : the static type of other must be a descendant class of v’s static type.
X e.g.,
40 of 62
jim: STUDENT ; rs: RESIDENT STUDENT; nrs: NON RESIDENT STUDENT create {STUDENT} jim.make (…)
create {RESIDENT STUDENT} rs.make (…)
create {NON RESIDENT STUDENT} nrs.make (…)
rs := jim nrs := jim jim := rs changes the dynamic type of jim to the dynamic type of rs
jim := nrs changes the dynamic type of jim to the dynamic type of nrs
Polymorphism and Dynamic Binding (1) Polymorphism : An object variable may have “multiple
possible shapes” (i.e., allowable dynamic types).
X Consequently, there are multiple possible versions of each feature
that may be called.
e.g., 3 possibilities of tuition on a STUDENT reference variable: In STUDENT: base amount
In RESIDENT STUDENT: base amount with premium rate
In NON RESIDENT STUDENT: base amount with discount rate
Dynamic binding : When a feature m is called on an object variable, the version of m corresponding to its “current shape” (i.e., one defined in the dynamic type of m) will be called.
jim: STUDENT; rs: RESIDENT STUDENT; nrs: NON STUDENT create {RESIDENT STUDENT} rs.make (…)
create {NON RESIDENT STUDENT} nrs.nrs (…)
jim := rs
jim.tuitoion; /* version in RESIDENT STUDENT */ jim := nrs
jim.tuition; /* version in NON RESIDENT STUDENT */
41 of 62
Polymorphism and Dynamic Binding (2.1)
1 2 3 4 5 6 7 8 9
10 11 12 13 14
test_polymorphism_students
local
jim: STUDENT
rs: RESIDENT STUDENT
nrs: NON RESIDENT STUDENT
do
create {STUDENT} jim.make (“J. Davis”)
create {RESIDENT STUDENT} rs.make (“J. Davis”) create {NON RESIDENT STUDENT} nrs.make (“J. Davis”) jim := rs
rs := jim
jim := nrs
rs := jim
end
In (L3, L7), (L4, L8), (L5, L9), ST = DT , so we may abbreviate: L7:
L8:
L9: 42 of 62
create jim.make (“J. Davis”)
create rs.make (“J. Davis”)
create nrs.make (“J. Davis”)
Polymorphism and Dynamic Binding (2.2)
test_dynamic_binding_students: BOOLEAN local
jim: STUDENT
rs: RESIDENT_STUDENT
nrs: NON_RESIDENT_STUDENT c: COURSE
do
create c.make (“EECS3311”, 500.0)
create {STUDENT} jim.make (“J. Davis”)
create {RESIDENT STUDENT} rs.make (“J. Davis”) rs.register (c)
rs.set_pr (1.5)
jim := rs
Result := jim.tuition = 750.0
check Result end
create {NON RESIDENT STUDENT} nrs.make (“J. Davis”) nrs.register (c)
nrs.set_dr (0.5)
jim := nrs
Result := jim.tuition = 250.0
end
43 of 62
Reference Type Casting: Motivation
1 2 3 4
local jim: STUDENT; rs: RESIDENT STUDENT
do create {RESIDENT STUDENT} jim.make (“J. Davis”)
rs := jim rs.setPremiumRate(1.5)
Line 2 is legal: RESIDENT_STUDENT is a descendant class of the static type of jim (i.e., STUDENT).
Line 3 is illegal: jim’s static type (i.e., STUDENT) is not a descendant class of rs’s static type (i.e., RESIDENT_STUDENT). Eiffel compiler is unable to infer that jim’s in
Line 4 is RESIDENT_STUDENT. [ ] Force the Eiffel compiler to believe so, by replacing L3, L4 by a
(which changes the ST of jim):
44 of 62
dynamic type
Undecidable
type cast
temporarily
check attached {RESIDENT STUDENT} jim as rs_jim then rs := rs_jim
rs.set_pr (1.5)
end
1 2 3 4
Reference Type Casting: Syntax
check attached {RESIDENT STUDENT} jim as rs_jim then rs := rs_jim
rs.set_pr (1.5)
end
L1 is an assertion: X
that is to be evaluated at .
If it evaluates to true, then the
of assigning “the cast version” of jim to a new variable rs jim. If it evaluates to false, then a runtime assertion violation occurs.
attached RESIDENT STUDENT jim
is a Boolean expression
runtime
as rs jim
expression has the effect
Dynamic Binding : Line 4 executes the correct version of set pr. It is approximately the same as following Java code:
if(jim instanceof ResidentStudent) { ResidentStudent rs = (ResidentStudent) jim; rs.set_pr(1.5);
}
else { throw new Exception(“Cast Not Done.”); }
45 of 62
X
Notes on Type Cast (1)
check attached {C} y then . . . end
always compiles
What if C is not an ancestor of y’s DT ?
A runtime assertion violation occurs!
ì y’s DT cannot fulfill the expectation of C.
46 of 62
Notes on Type Cast (2)
Given v of static type ST , it is violation-free to cast v to C , as long as C is a descendant or ancestor class of ST .
Why Cast?
X Without cast, we can only call features defined in ST on v.
X By casting v to C , we create an alias of the object pointed by v,
with the new static type C .
All features that are defined in C can be called.
my_phone: IOS
create {IPHONE 11 PRO} my_phone.make
— can only call features defined in IOS on myPhone
— dial, surf_web, facetime quick_take, skype, side_sync, zoomage check attached {SMART PHONE} my_phone as sp then
— can now call features defined in SMART_PHONE on sp
— dial, surf_web facetime, quick_take, skype, side_sync, zoomage end
check attached {IPHONE 11 PRO} my_phone as ip11_pro then
— can now call features defined in IPHONE_11_PRO on ip11_pro
— dial, surf_web, facetime, quick_take skype, side_sync, zoomage
end
47 of 62
Notes on Type Cast (3)
A cast triggers an assertion
violation if C is not along the ancestor path of v’s DT .
test_smart_phone_type_cast_violation local mine: ANDROID
do create {HUAWEI} mine.make
— ST of mine is ANDROID; DT of mine is HUAWEI
check attached {SMART PHONE} mine as sp then … end — ST of sp is SMART_PHONE; DT of sp is HUAWEI
check attached {HUAWEI} mine as huawei then … end — ST of huawei is HUAWEI; DT of huawei is HUAWEI check attached {SAMSUNG} mine as samsung then … end — Assertion violation
— ì SAMSUNG is not ancestor of mine’s DT (HUAWEI)
check attached {HUAWEI P30 PRO} mine as p30_pro then … end — Assertion violation
— ì HUAWEI_P30_PRO is not ancestor of mine’s DT (HUAWEI)
end
48 of 62
check attached {C} v as …
Polymorphism: Feature Call Arguments (1)
1 2 3 4 5
class STUDENT_MANAGEMENT_SYSTEM {
ss : ARRAY[STUDENT] — ss[i] has static type Student add_s (s: STUDENT) do ss[0] := s end
add_rs (rs: RESIDENT STUDENT) do ss[0] := rs end add_nrs (nrs: NON RESIDENT STUDENT) do ss[0] := nrs end
L4: is valid. ì RHS’s ST RESIDENT STUDENT is a descendant class of LHS’s ST STUDENT.
Say we have a STUDENT MANAGEMENT SYSETM object sms:
X ì call by value , attempts the following assignment (i.e., replace parameter rs by a copy of argument o):
X Whether this argument passing is valid depends on o’s static type. Rule: In the signature of a feature m, if the type of a parameter is class C, then we may call feature m by passing objects whose
static types are C’s descendants. 49 of 62
ss[0]:=rs
sms.add rs(o)
rs := o
Polymorphism: Feature Call Arguments (2)
test_polymorphism_feature_arguments
local
s1, s2, s3: STUDENT
rs: RESIDENT STUDENT ; nrs: NON RESIDENT STUDENT sms: STUDENT_MANAGEMENT_SYSTEM
do
create sms.make
create {STUDENT} s1.make (“s1”)
create {RESIDENT_STUDENT} s2.make (“s2”)
create {NON_RESIDENT_STUDENT} s3.make (“s3”)
create {RESIDENT_STUDENT} rs.make (“rs”)
create {NON_RESIDENT_STUDENT} nrs.make (“nrs”)
sms.add_s (s1) sms.add_s (s2) sms.add_s (s3) sms.add_s (rs) sms.add_s (nrs)
sms.add_rs (s1) sms.add_rs (s2) sms.add_rs (s3) sms.add_rs (rs) sms.add_rs (nrs)
sms.add_nrs (s1) sms.add_nrs (s2) sms.add_nrs (s3) sms.add_nrs (rs) sms.add_nrs (nrs)
end
50 of 62
Why Inheritance:
A Polymorphic Collection of Students
How do you define a class STUDENT MANAGEMENT SYSETM that contains a list of resident and non-resident students?
class STUDENT_MANAGEMENT_SYSETM students: LINKED_LIST[STUDENT] add_student(s: STUDENT)
do
students.extend (s) end
registerAll (c: COURSE) do
across
students as s
loop
s.item.register (c) end
end end
51 of 62
Polymorphism and Dynamic Binding: A Polymorphic Collection of Students
test_sms_polymorphism: BOOLEAN local
rs: RESIDENT_STUDENT
nrs: NON_RESIDENT_STUDENT
c: COURSE
sms: STUDENT_MANAGEMENT_SYSTEM
do
create rs.make (“Jim”)
rs.set_pr (1.5)
create nrs.make (“Jeremy”)
nrs.set_dr (0.5)
create sms.make
sms.add_s (rs)
sms.add_s (nrs)
create c.make (“EECS3311”, 500)
sms.register_all (c)
Result := sms.ss[1].tuition = 750 and sms.ss[2].tuition = 250
end
52 of 62
Polymorphism: Return Values (1)
1 2 3 4 5 6 7 8 9
10 11 12
class STUDENT_MANAGEMENT_SYSTEM { ss: LINKED_LIST[STUDENT]
add_s (s: STUDENT)
do
ss.extend (s) end
get_student(i: INTEGER): STUDENT require 1 <= i and i <= ss.count do
Result := ss[i] end
end
L2: ST of each stored item (ss[i]) in the list:
L3: ST of input parameter s:
L7: ST of return value (Result) of get student:
L11: ss[i]’s ST is descendant of Result’ ST .
Question: What can be the dynamic type of s after Line 11?
Answer: All descendant classes of Student. 53 of 62
[STUDENT] [STUDENT] [STUDENT]
Polymorphism: Return Values (2)
1 2 3 4 5 6 7 8 9
10 11 12 13
test_sms_polymorphism: BOOLEAN local
rs: RESIDENT_STUDENT ; nrs: NON_RESIDENT_STUDENT
c: COURSE ; sms: STUDENT_MANAGEMENT_SYSTEM do
create rs.make ("Jim") ; rs.set_pr (1.5)
create nrs.make ("Jeremy") ; nrs.set_dr (0.5)
create sms.make ; sms.add_s (rs) ; sms.add_s (nrs) create c.make ("EECS3311", 500) ; sms.register_all (c) Result :=
get_student(1).tuition = 750 and get_student(2).tuition = 250
end
L11: get student(1)’s dynamic type? L11: Version of tuition?
L12: get student(2)’s dynamic type?
L12: Version of tuition? 54 of 62
[RESIDENT_STUDENT]
[RESIDENT_STUDENT] [NON_RESIDENT_STUDENT] [NON_RESIDENT_STUDENT]
Design Principle: Polymorphism
When declaring an attribute
Choose static type which “accumulates” all features that you predict you will want to call on a.
e.g., Choose if you do not intend to be specific about which kind of student s might be.
Let dynamic binding determine at runtime which version of tuition will be called.
What if after declaring you find yourself often needing to cast s to RESIDENT STUDENT in order to access premium rate?
Your design decision should have been: Same design principle applies to:
X Type of feature parameters:
X Type of queries: 55 of 62
s: STUDENT
T
s: STUDENT
a: T
check attached {RESIDENT_STUDENT} s as rs then rs.set_pr(...) end
s:RESIDENT_STUDENT
f(a: T)
q(...): T
Static Type vs. Dynamic Type: When to consider which?
Whether or not an OOP code compiles depends only on the static types of relevant variables.
ì Inferring the dynamic type statically is an undecidable problem that is inherently impossible to solve.
The behaviour of Eiffel code being executed at runtime
e.g., which version of method is called
e.g., if a check attached {...} as ... then ... end assertion error will occur
depends on the dynamic types of relevant variables.
Best practice is to visualize how objects are created (by drawing boxes) and variables are re-assigned (by drawing arrows).
56 of 62
Summary: Type Checking Rules
CODE
CONDITION TO BE TYPE CORRECT
x := y
y’s ST a descendant of x’s ST
x.f(y)
Feature f defined in x’s ST
y’s ST a descendant of f’s parameter’s ST
z := x.f(y)
Feature f defined in x’s ST
y’s ST a descendant of f’s parameter’s ST ST of m’s return value a descendant of z’s ST
check attached {C} y
Always compiles
check attached {C} y as temp then x := temp end
C a descendant of x’s ST
check attached {C} y as temp then x.f(temp) end
Feature f defined in x’s ST
C a descendant of f’s parameter’s ST
check attached {C} y then . . . end
Even if always compiles, a runtime assertion error occurs if C is not an ancestor of y’s DT!
57 of 62
Index (1)
Aspects of Inheritance
Why Inheritance: A Motivating Example The COURSE Class
No Inheritance: No Inheritance:
No Inheritance: No Inheritance: Issues with the
No Inheritance:
RESIDENT STUDENT Class
NON RESIDENT STUDENT Class Testing Student Classes
Student Classes Maintainability of Code (1) Maintainability of Code (2)
No Inheritance:
No Inheritance:
A Collection of Various Kinds of Students
Inheritance Architecture
Inheritance: The STUDENT Parent Class 58 of 62
Index (2) Inheritance:
The RESIDENT STUDENT Child Class Inheritance:
The NON RESIDENT STUDENT Child Class Inheritance Architecture Revisited
Using Inheritance for Code Reuse Testing the Two Student Sub-Classes Static Type vs. Dynamic Type Inheritance Architecture Revisited Polymorphism: Intuition (1) Polymorphism: Intuition (2) Polymorphism: Intuition (3)
Dynamic Binding: Intuition (1)
Dynamic Binding: Intuition (2)
DbC: Contract View of Supplier
59 of 62
Index (3)
ES TEST: Expecting to Fail Postcondition (1) ES TEST: Expecting to Fail Postcondition (2.1) ES TEST: Expecting to Fail Postcondition (2.2) Exercise
Multi-Level Inheritance Architecture (1) Multi-Level Inheritance Architecture (2) Inheritance Forms a Type Hierarchy Inheritance Accumulates Code for Reuse Substitutions via Assignments
Rules of Substitution
Reference Variable: Static Type
Reference Variable: Dynamic Type
Reference Variable:
Changing Dynamic Type (1)
60 of 62
Index (4)
Reference Variable:
Changing Dynamic Type (2)
Polymorphism and Dynamic Binding (1) Polymorphism and Dynamic Binding (2.1) Polymorphism and Dynamic Binding (2.2) Reference Type Casting: Motivation Reference Type Casting: Syntax
Notes on Type Cast (1)
Notes on Type Cast (2)
Notes on Type Cast (3)
Polymorphism: Feature Call Arguments (1) Polymorphism: Feature Call Arguments (2) Why Inheritance:
A Polymorphic Collection of Students
61 of 62
Index (5)
Polymorphism and Dynamic Binding: A Polymorphic Collection of Students
Polymorphism: Return Values (1)
Polymorphism: Return Values (2)
Design Principle: Polymorphism
Static Type vs. Dynamic Type: When to consider which?
Summary: Type Checking Rules
62 of 62