Oop Slides
Oop Slides
uk
Selection of exercises roughly mapped to
lectures
I want to write more so let me know where
you see holes
Attempt to get a bit closer to what you
would do in industry
Git version control system
Automated testing
4
Objectives
To understand the workflow and tools to
complete a practical exercise
13
We’d like to use your code for research
Research into teaching and learning is
important!
We want your consent to use your code
and share it with others
We will ‘anonymise’ it
Consent is optional and it has no impact
on your grades or teaching if you do not
15
Practical exercises are linked online
Go to the course webpages to find links to
the practical exercises
Follow the link and start the task
16
Software licensing and copyright
Complicated area…
The default is that if you write software you own
the copyright and other people can’t copy it
We add licenses to make it clear what people can
and can’t do
The initial code for the tasks is Apache 2
Licensed
The system assumes your changes will be
licensed the same...but they don’t have to be
Apache 2 License lets you do almost anything
Except remove or change the license
Demo: licenses on your code
17
Using an IDE is recommended!
I’ll use IntelliJ here but you can use
whatever you like
You only need the (free) ‘community
edition’
IntelliJ has built-in support for git but you
can use the command line or other tools if
you prefer
Sourcetree on Mac is really nice
20
Types of Languages
28
Top 20 Languages 2016
29
Top 20 Languages 2016 (Cont)
30
Top 20 Languages 2016 (Cont Cont)
31
Top 20 Languages 2016 (Cont Cont Cont)
32
ML as a Functional Language
Functional languages are a subset of declarative
languages
ML is a functional language
It may appear that you tell it how to do everything, but
you should think of it as providing an explicit example
of what should happen
The compiler may optimise i.e. replace your
implementation with something entirely different but
100% equivalent.
Maths:m(x,y) = xy
ML: fun m(x,y) = x*y;
Java:
int y = 7; Side e(ect
int m(int x) {
y=y+1;
return x*y;
}
34
void Procedures
A void procedure returns nothing:
int count=0;
Void is not quite the
same as unit in ML
void addToCount() {
count=count+1;
}
35
Control Flow: Looping
while( boolean_expression )
36
Control Flow: Branching I
Branching statements interrupt the current control flow
return
Used to return from a function at any point
38
Control Flow: Branching II
Branching statements interrupt the current control flow
break
Used to jump out of a loop
39
Control Flow: Branching III
Branching statements interrupt the current control flow
continue
Used to skip the current iteration in a loop
40
Immutable to Mutable Data
ML
- val x=5; ML is a language of expressions
> val x = 5 : int
- x=7; Java is a language of statements and expressions
> val it = false : bool
- val x=9;
> val x = 9 : int
Java
Evaluates to the value 7 with type int
int x=5;
x=7;
int x=9;
for(int i=0;i<10;i++) { Does not evaluate to a value and has no type
[Link](i);
}
Demo: returning vs prin9ng 41
Types and Variables
Java and C++ have limited forms of type inference
var x = 512;
int y = 200;
int z = x+y;
The high-level language has a series of primitive (built-in)
types that we use to signify what’s in the memory
The compiler then knows what to do with them
E.g. An “int” is a primitive type in C, C++, Java and many
languages. In Java it is a 32-bit signed integer.
A variable is a name used in the code to refer to a specific
instance of a type
x,y,z are variables above
They are all of type int
42
E.g. Primitive Types in Java
“Primi9ve” types are the built in ones.
They are building blocks for more complicated types that we will be
looking at soon.
boolean – 1 bit (true, false)
char – 16 bits
Widening
byte – 8 bits as a signed integer (-128 to 127) Vs
short – 16 bits as a signed integer Narrowing
int – 32 bits as a signed integer
long – 64 bits as a signed integer
Hoat – 32 bits as a Hoa9ng point number
double – 64 bits as a Hoa9ng point number
fun myfun(a,b) = …;
45
Custom Types
46
State and Behaviour
type 'a seq =
| Nil
| Cons of 'a * (unit -> 'a seq);
fun hd (Cons(x,_)) = x;
47
State and Behaviour
type 'a seq =
| Nil
| Cons of 'a * (unit -> 'a seq);
fun hd (Cons(x,_)) = x;
State Behaviour
Fields Func9ons
Instance Variables Methods
Proper9es Procedures
Variables
Members
49
Classes, Instances and Objects
Classes can be seen as templates for representing
various concepts
We create instances of classes in a similar way.
e.g.
MyCoolClass m = new MyCoolClass();
MyCoolClass n = new MyCoolClass();
makes two instances of class MyCoolClass.
An instance of a class is called an object
50
Defining a Class
51
Constructors
MyObject m = new MyObject();
You will have noticed that the RHS looks rather like a function
call, and that's exactly what it is.
52
Constructors with Arguments
public class Vector3D {
Hoat x;
Hoat y;
Hoat z;
// ...
}
Vector3D() {
x=0.f;
y=0.f;
z=0.f;
}
Vector3D v = new Vector3D(1.f,0.f,2.f);
// ...
}
Vector3D v2 = new Vector3D(); 54
Default Constructor
public class Vector3D {
Hoat x;
Hoat y;
Hoat z;
} If you don’t ini9alise a Zeld it
gets set to the ‘zero’ value for
Vector3D v = new Vector3D(); that type (don’t do this)
No constructor provided
So blank one generated with
no arguments
55
Class-Level Data and Functionality I
A static field is created only once in the program's execution,
despite being declared as part of a class
58
Class-Level Data and Functionality II
0.2
0.2 Shared between
0.2
instances
Space efficient
17.5
0.2 17.5
60
What Not to Do
Your ML has doubtless been one big file where
you threw together all the functions and value
declarations
Lots of C programs look like this :-(
We could emulate this in OOP by having one
class and throwing everything into it
61
Identifying Classes
We want our class to be a grouping of conceptually-
related state and behaviour
One popular way to group is using grammar
Noun → Object
Verb → Method
62
UML: Representing a Class Graphically
Ques9on
“+” means
public access
63
The has-a Association
Quiz 1 0...* Ques9on
Modularity
Code Re-Use
Encapsulation
Inheritance (lecture 5)
Polymorphism (lecture 6)
66
Modularity and Code Re-Use
You've long been taught to break down complex
problems into more tractable sub-problems.
Each class represents a sub-unit of code that (if
written well) can be developed, tested and updated
independently from the rest of the code.
Indeed, two classes that achieve the same thing
(but perhaps do it in different ways) can be swapped
in the code
Properly developed classes can be used in other
programs without modification.
67
Encapsulation I
class Student {
int age;
};
void main() {
Student s = new Student();
[Link] = 21;
boolean setAge(int a) {
if (a>=0 && a<130) {
age=a;
return true;
}
return false;
}
void main() {
Student s = new Student();
[Link](21);
} 69
Encapsulation III
Encapsula9on =
1) hiding internal state
2) bundling methods with state
70
Access Modifiers
package X X
(Java) Surprising!
protected X X X
public X X X X
71
Immutability
Everything in ML was immutable (ignoring the
reference stuff). Immutability has a number of
advantages:
Easier to construct, test and use
Can be used in concurrent contexts
Allows lazy instantiation
We can use our access modifiers to create
immutable classes
If you mark a variable or field as ‘final’ then it can’t
be changed after initalisation
Demo: NotImmutable
72
Parameterised Classes
ML's polymorphism allowed us to specify functions that could
be applied to multiple types
73
Creating Parameterised Types
These just require a placeholder type
class Vector3D<T> {
private T x;
private T y;
74
Generics use type-erasure
class Vector3D<T> { class Vector3D {
private T x; private Object x;
private T y; private Object y;
void setX(T nx) {x=nx;} a_er type void setX(Object nx) {x=nx;}
void setY(T ny) {y=ny;} checking void setY(Object ny) {y=ny;}
this
} }
compiles
to
------>
Vector3D<Integer> v = Vector3D v = new Vector3D();
new Vector3D<>(); Integer x = (Integer)[Link]();
Integer x = [Link](); [Link]((Object)4);
[Link](4);
75
Each cell is a ‘byte’ 100 Call stack
104
108
32-bit architecture 112
=> 4 bytes to a word 116
120
124
Address
128
(usually written
in hexadecimal) 132
e.g. 0x07C 136
140
144
148
152
Each row is a ‘word’ 156
160
164
2 168 Heap
100
1 void f(int x) {
104
2 char c = 'a';
108
3 long l = 1234;
112
4 int i = 10;
116
5 }
120
6
124
>> 7 f(4);
128
132
136
140
144
148
152
156
160
164
3 This example is in C/C++ 168
100 4 0 0 0 x
>> 1 void f(int x) { c
104
2 char c = 'a';
108 l
3 long l = 1234;
112
4 int i = 10;
116 i
5 }
120
6
124
7 f(4);
128
132
136
140
144
148
152
156
160
164
4 168
100 4 0 0 0 x
1 void f(int x) { 97 . . . c
104
>> 2 char c = 'a';
108 l
3 long l = 1234;
112
4 int i = 10;
116 i
5 }
120
6
124
7 f(4);
128
132
136
140
144
148
152
156
160
164
5 168
100 4 0 0 0 x
1 void f(int x) {
104 97 . . . c
2 char c = 'a';
108 210 4 0 0 l
>> 3 long l = 1234;
112 0 0 0 0
4 int i = 10;
116 i
5 }
120
6
124
7 f(4);
128
132
136
1234 is bigger than one byte 140
144
1234 & 0xFF = 210 148
1234 >> 8 = 4 152
156
160
164
6 168
100 4 0 0 0 x
1 void f(int x) {
104 97 . . . c
2 char c = 'a';
108 210 4 0 0 l
3 long l = 1234;
112 0 0 0 0
>> 4 int i = 10;
116 10 0 0 0 i
5 }
120
6
124
7 f(4);
128
132
136
140
144
148
152
156
160
164
7 168
100 4 x
1 void f(int x) { ‘a’ c
104
2 char c = 'a';
108 l
3 long l = 1234; 1234
112
>> 4 int i = 10; 10
116 i
5 }
120
6
124
7 f(4);
128
132
136
140
144
148
152
156
160
164
8 168
100 1 i
1 void f() { 2 j
104
2 int i = 1;
108 3 k
3 int j = 2; p
112 100
4 int k = 3; q
116 108
5 int* p = &i;
120
6 int* q = &k;
124
7 }
128
132
136
* on a LHS means
‘its a pointer’ 140
144
& on a RHS means 148
‘take the address of’ 152
156
160
164
9 168
100 1 i
1 void f() { 2 j
104
2 int i = 1;
108 3 k
3 int j = 2; p
112 100
4 int k = 3; q
116 108
5 int* p = &i;
120 104 r
6 int* q = &k;
124
7 int* r = p + 1;
128
8 }
132
136
140
We can do arithmetic on 144
pointers (based on the 148
datatype size) 152
156
160
164
10 168
100 1 i
1 void f() { 2 j
104
2 int i = 1;
108 3 k
3 int j = 2; p
112 100
4 int k = 3; q
116 108
5 int* p = &i;
120 104 r
6 int* q = &k; 2 l
124
7 int* r = p + 1;
128
8 int l = *r;
132
}
136
140
* on the RHS means 144
‘dereference’ i.e. follow 148
the pointer. 152
156
160
164
11 168
100 1 i
1 void f() { 2 j
104
2 int i = 1;
108 3 k
3 int j = 2; p
112 100
4 int k = 3; q
116 108
5 int* p = &i;
120 104 r
6 int* q = &k; 2 l
124
7 int* r = p + 1;
128 100 m
8 int l = *r;
132
9 int m = *(q + 1);
136
10 }
140
144
148
Nothing will stop you 152
making mistakes! 156
160
164
12 168
100 160 c
1 int len() { 104 160 p
2 char[] c = new[] 108
3 {'a','b','\0'};
112
4 char* p = c;
116
5 }
120
6
124
7
128
8
132
9
136
In C++ you can choose whether 140
you want to allocate on the stack 144
or the heap 148
152
156
160 ‘a’
164 ‘b’
13 168 ‘\0’
Items on the stack exist only for the duration of your function call
14
>> 1 static int sum() {
2 int s = sum(3);
3 return s;
4 }
5
6 static int sum(int n) {
7 if (n == 0) {
8 return 0;
9 }
10 int m = sum(n - 1);
11 int r = m + n;
12 return r;
13 }
>> 1 static int sum() { sum()
s
2 int s = sum(3);
3 return s;
4 }
5
6 static int sum(int n) {
7 if (n == 0) {
8 return 0;
9 }
10 int m = sum(n - 1);
11 int r = m + n;
12 return r;
13 }
1 static int sum() { sum()
>> 2 s
int s = sum(3);
3 return s;
4 }
5
6 static int sum(int n) {
7 if (n == 0) {
8 return 0;
9 }
10 int m = sum(n - 1);
11 int r = m + n;
12 return r;
13 }
1 static int sum() { sum()
>> 2 s
int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 arg1
5
6 static int sum(int n) {
7 if (n == 0) {
8 return 0;
9 }
10 int m = sum(n - 1);
11 int r = m + n;
12 return r;
13 }
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
>> 6 static int sum(int n) { r
7 if (n == 0) {
8 return 0;
9 }
10 int m = sum(n - 1);
11 int r = m + n;
12 return r;
13 }
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
>> 7 if (n == 0) {
8 return 0;
9 }
10 int m = sum(n - 1);
11 int r = m + n;
12 return r;
13 }
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) {
8 return 0;
9 }
>> 10 int m = sum(n - 1);
11 int r = m + n;
12 return r;
13 }
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
8 return 0; 2 arg1
9 }
>> 10 int m = sum(n - 1);
11 int r = m + n;
12 return r;
13 }
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
>> 6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
10 int m = sum(n - 1); r
11 int r = m + n;
12 return r;
13 }
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
>> 7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
10 int m = sum(n - 1); r
11 int r = m + n;
12 return r;
13 }
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
>> 10 int m = sum(n - 1); r
11 int r = m + n;
12 return r;
13 }
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
>> 10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
12 return r; 1 arg1
13 }
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
>> 6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
12 return r; 1 n
13 } m
r
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
>> 7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
12 return r; 1 n
13 } m
r
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
>> 10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
12 return r; 1 n
13 } m
r
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
>> 10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
12 return r; 1 n
13 } m
r
sum(0) 10 return
0 arg1
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
>> 6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
12 return r; 1 n
13 } m
r
sum(0) 10 return
0 n
m
r
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
>> 7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
12 return r; 1 n
13 } m
r
sum(0) 10 return
0 n
m
r
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
>> 8 return 0; 2 n
9 } m
10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
12 return r; 1 n
13 } m
r
sum(0) 10 return
0 n
m
r
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
>> 8 return 0; 2 n
9 } m
10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
12 return r; 1 n
13 } m
r
sum(0) 10 return
0 n
m
r
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
>> 8 return 0; 2 n
9 } m
10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
12 return r; 1 n
13 } m
r
sum(0) 10 return
Return the value 0 and then 0 n
execute instruc9on 10 m
r
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
>> 10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
12 return r; 1 n
13 } 0 m
r
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
10 int m = sum(n - 1); r
>> 11 int r = m + n; sum(1) 10 return
12 return r; 1 n
13 } 0 m
1 r
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 m
6 static int sum(int n) { r
7 if (n == 0) { sum(2) 10 return
8 return 0; 2 n
9 } m
10 int m = sum(n - 1); r
11 int r = m + n; sum(1) 10 return
>> 12 return r; 1 n
13 } 0 m
1 r
16
1 static void test() { 3 i
2 int i = 3; <addr> a
3 int[] a = new int[] {1,2}; <addr> s
4 String s = "a";
5 }
<string stuff>
‘a’
<array stuff>
This example is in Java 1
17 2
1 static void test() {
2 int i = 3;
3 int[] a = new int[] {1,2};
4 String s = "a";
5 }
18
References in C++ are a completely different concept!
19
1 static void test() { 3 i
>> 2 int i = 3;
3 int* k = &i;
4 int& j = i;
5 }
21
1 static void test() { 3 i,j
2 int i = 3; <addr> k
3 int* k = &i;
>> 4 int& j = i;
5 }
22
Recap for Java
●
Primitive types on the stack
●
Everything else on the heap
●
References are values on the stack that ‘point’ to
somewhere on the heap
●
References are like pointers but you can’t do artithmetic
on them
●
Java references are not much like C++ references
23
Distinguishing References and Pointers
Pointers References
in Java
110
References in Java
Declaring unassigned
SomeClass ref = null; // explicit
Defining/assigning
// Assign
SomeClass ref = new ClassRef();
114
Inheritance I
class Student { There is a lot of duplication here
private int age;
private String name;
Conceptually there is a hierarchy that we're
private int grade; not really representing
... Both Lecturers and Students are people
}
(no, really).
class Lecturer { We can view each as a kind of
private int age;
private String name; specialisation of a general person
private int salary; They have all the properties of a person
…
} But they also have some extra stuff
specific to them
116
Inheritance II
class Person { We create a base class (Person)
protected int age; and add a new notion: classes can
protected String name;
...
inherit properties from it
} Both state, functionality and type
class Student extends Person { We say:
private int grade; Person is the superclass of
...
} Lecturer and Student
Lecturer and Student subclass
class Lecturer extends Person {
private int salary; Person
...
} ‘extends’ in Java gives you both code- and type-inheritance
If you mark a class ‘Znal’ then it can’t be extended and ‘Znal’ methods
can’t be overridden
117
Liskov Substitution Principle
If S is a subtype of T then objects of type T
may be replaced with objects of type S
Student is a subtype of Person so
anywhere I can have a Person I can have
a Student instead
118
Representing Inheritance Graphically
Person Also known as an “is-a” rela9on
Specialise
Generalise
Student Lecturer
exam_score
salary
int i = 7;
Hoat f = (Hoat) i; // f==7.0
double d = 3.2;
int i2 = (int) d; // i2==3
120
Widening
Person Student is-a Person
Hence we can use a Student object
anywhere we want a Person object
Can perform widening conversions
Student (up the tree)
Student
OK because underlying object
really is a Student
123
Fields and Inheritance: Shadowing
class A { public int x; }
class B extends A {
public int x; ‘this’ is a reference to the current object
}
‘super’ is a reference to the parent object
class C extends B {
public int x; all classes extend Object (capital O)
public void ac9on() {
if you write ‘class A {}’ you actually get
// Ways to set the x in C
‘class extends Object {}’
x = 10;
this.x = 10;
Object a = new A(); // subs9tu9on principle
// Ways to set the x in B
super.x = 10;
((B)this).x = 10; Don’t write code like this. No-one will
understand you!
// Ways to set the x in A
((A)this.x = 10;
}
} 124
Methods and Inheritance: Overriding
We might want to require that every Person can dance. But the way
a Lecturer dances is not likely to be the same as the way a Student
dances...
Know the di(erence: overriding vs overloading
class Person {
Person deZnes an
public void dance() { original implementa9on
jiggle_a_bit(); of dance()
}
}
127
Representing Abstract Classes
Person
Student Lecturer
+ dance() + dance()
128
Polymorphic Methods
Polymorphism: values and variables can have more than one type
int eval(Expression e) {
can be Literal, Mult or Plus
} 131
Polymorphic Concepts I
Static polymorphism
Decide at compile-time
Since we don't know what the true type of the
object will be, we just run the method based on
its static type
Star
+ draw()
134
Demo
The Canonical Example II
Shape Option 2
Keep a single list of Shape references
Figure out what each object really is,
narrow the reference and then draw()
Circle
for every Shape s in myShapeList
+ draw()
if (s is really a Circle)
Circle c = (Circle)s;
Square
[Link]();
+ draw()
else if (s is really a Square)
Square sq = (Square)s;
Oval
+ draw()
[Link]();
else if...
Star What if we want to add a new shape?
+ draw()
135
Demo
The Canonical Example III
Shape
Option 3 (Polymorphic)
- x_posi9on: int Keep a single list of Shape references
- y_posi9on: int
Let the compiler figure out what to do
+ draw()
with each Shape reference
Circle
+ draw()
Star
+ draw()
136
Demo
Implementations
Java
All methods are dynamic polymorphic.
Python
All methods are dynamic polymorphic.
C++
Only functions marked virtual are dynamic polymorphic
137
Harder Problems
Given a class Fish and a class DrawableEntity, how do we
make a BlobFish class that is a drawable fish?
DrawableEn9ty
0..1 0..1
DrawableEn9ty BlobFish Fish
Fish
X Conceptually wrong
BlobFish
X Dependency
between two
independent
concepts
138
Multiple Inheritance
Fish DrawableEn9ty If we multiple inherit, we capture
the concept we want
+ swim() + draw()
BlobFish inherits from both and
is-a Fish and is-a DrawableEntity
C++:
class Fish {…}
class DrawableEn9ty {…}
BlobFish But...
+ swim()
+ draw()
139
Multiple Inheritance Problems
Fish DrawableEn9ty What happens here? Which of
the move() methods is inherited?
+ move() + move() Have to add some grammar to
make it explicit
C++:
Fish DrawableEn9ty
+ move() + move()
BlobFish
????
141
The diamond problem
CountableEn9ty CountableEn9ty CountableEn9ty
BlobFish BlobFish
???? ????
142
Fixing with Abstraction
Fish [Link] Actually, this problem
+ move() + move() goes away if one or more
of the conflicting
methods is abstract
BlobFish
+ move()
143
Java's Take on it: Interfaces
Classes can have at most one parent. Period.
But special 'classes' that are totally abstract can do
multiple inheritance – call these interfaces
interface Drivable { adjec9ve
<<interface>> <<interface>> public void turn();
Drivable Iden7&able public void brake();
} This is type
+ turn() + getIden7&er() inheritance
+ brake() interface Iden9Zable { (not code
public void getIden9Zer();
inheritance)
}
interface Foo {
int x = 1;
int y();
}
means
interface Foo {
public static final int x = 1;
public int y();
}
145
Interfaces can have default methods
interface Foo {
int x = 1;
int y();
default int yPlusOne() {
return y() + 1;
}
}
Load
[Link]
No Is MyObject already loaded
in memory?
Create
[Link] Yes
object
Allocate memory
for object
Allocate any
sta9c Zelds and run
sta9c ini9aliser Run non-sta9c
blocks ini9aliser blocks
Run constructor
demo ObjectConstruc9on
sta9c ini9alisa9on is done demo InheritedConstruc9on
in textual order
149
Constructor Chaining
When you construct an object of a type with parent
classes, we call the constructors of all of the parents
in sequence
Student s = new Student();
Animal
1. Call Animal()
Person
2. Call Person()
Student
public Student () {
+Student()
super(“Bob”);
}
Demo: NoDefaultConstructor
152
Deterministic Destruction
Objects are created, used and (eventually) destroyed. Destruction is very language-
specific
Deterministic destuction is what you would expect
Objects are deleted at predictable times
Perhaps manually deleted (C++):
void UseRawPointer()
{
MyClass *mc = new MyClass();
// ...use mc...
delete mc;
}
Or auto-deleted when out of scope (C++):
In C++ this means
void UseSmartPointer() create a new instance
{ of MyClass on the stack
MyClass mc; using the default
// ...use mc... constructor
} // mc deleted here
153
Destructors
Most OO languages have a notion of a destructor too
Gets run when the object is destroyed
Allows us to release any resources (open files, etc) or memory
that we might have created especially for the object
private :
This is called RAII = Resource Acquisi9on Is Ini9alisa9on
FILE *Zle;
}
154
Non-Deterministic Destruction
Deterministic destruction is easy to understand and seems simple
enough. But it turns out we humans are rubbish of keeping track of
what needs deleting when
We either forget to delete (→ memory leak) or we delete multiple
times (→ crash)
We can instead leave it to the system to figure out when to delete
“Garbage Collection”
The system somehow figures out when to delete and does it for us
In reality it needs to be cautious and sure it can delete. This leads
to us not being able to predict exactly when something will be
deleted!!
This is the Java approach!!
Demo: Finalizer
155
What about Destructors?
Conventional destructors don’t make
sense in non-deterministic systems
When will they run?
Will they run at all??
Instead we have finalisers: same concept
but they only run when the system deletes
the object (which may be never!)
Java provides try-with-resources as an
alternative to RAII
Demo: TryWithResources
156
Garbage Collection
So how exactly does garbage collection work? How can a
system know that something can be deleted?
The garbage collector is a separate process that is constantly
monitoring your program, looking for things to delete
Running the garbage collector is obviously not free. If your
program creates a lot objects, you will soon notice the collector
running
Can give noticeable pauses to your program!
But minimises memory leaks (it does not prevent them…)
Keywords:
‘Stop the world’ - pause the program when collecting garbage
‘incremental’ - collect in multiple phases and let the program
run in the gaps
‘concurrent’ - no pauses in the program
Demo: Leak
157
Mark and sweep
Start with a list of all references you can get to
Follow all references recursively, marking each object
Delete all objects that were not marked
object
object
x
y object
z
object
object
162
Java's Collections Framework
<<interface>> Important chunk of the class library
[Link]
Iterable
A collection is some sort of grouping of things
(objects)
<<interface>> Usually when we have some grouping we want
[Link]
to go through it (“iterate over it”)
164
Lists
<<interface>> List
C A
An ordered collection of elements that may contain
duplicates
LinkedLIst: linked list of elements
B B
ArrayList: array of elements (efficient access)
Vector: Legacy, as ArrayList but threadsafe Iterable
Collec9on
List<Double> ll = new ArrayList<>();
[Link](1.0); List
[Link](0.5);
[Link](3.7); LinkedList ArrayList Vector
[Link](0.5);
[Link](1); // get element 2 (==3.7)
legacy
good default
choice
165
Queues
<<interface>> Queue
An ordered collection of elements that may contain
duplicates and supports removal of elements from the head
of the queue
offer() to add to the back and poll() to take from the front
C
LinkedList: supports the necessary functionality
PriorityQueue: adds a notion of priority to the queue so more A
important stuff bubbles to the top
B
B
Queue<Double> ll = new LinkedList<>();
ll.o(er(1.0);
ll.o(er(0.5);
[Link](); // 1.0
[Link](); // 0.5
166
Maps
<<interface>> Map
Like dictionaries in ML
K1
Maps key objects to value objects A
Keys must be unique K3 K2
B
Values can be duplicated and B
(sometimes) null.
TreeMap: keys kept in order
HashMap: Keys not in order, efficient
(see Algorithms)
169
Iteration
for loop
LinkedList<Integer> list = new LinkedList<Integer>();
...
for (int i=0; i<[Link](); i++) {
Integer next = [Link](i);
}
(p1==p1);
True
176
Value Equality
Use the equals() method in Object
Default implementation just uses reference equality (==)
so we have to override the method
public EqualsTest {
public int x = 8;
Learn the ‘equals’ contract
@Override
public boolean equals(Object o) {
EqualsTest e = (EqualsTest)o;
return (this.x==e.x);
}
178
Comparable<T> Interface I
int compareTo(T obj);
179
Comparable<T> Interface II
public class Point implements Comparable<Point> {
private Znal int mX;
private Znal int mY;
public Point (int, int y) { mX=x; mY=y; } implemen9ng Comparable
deZnes a natural ordering
// sort by y, then x for your class
public int compareTo(Point p) {
if ( mY>[Link]) return 1; ideally this should be
else if (mY<[Link]) return -1; consistent with equals i.e.
else { [Link](y) == 0 <=> [Link](y)
if (mX>[Link]) return 1;
else if (mX<[Link]) return -1; must deZne a total order
else return 0.
}
}
}
181
Comparator<T> Interface II
public class Person implements Comparable<Person> {
private String mSurname;
private int mAge;
public int compareTo(Person p) { delegate to the Zeld’s
return [Link]([Link]); compareTo method
}
}
…
ArrayList<Person> plist = …;
…
[Link](plist); // sorts by surname
[Link](plist, new AgeComparator()); // sorts by age
182
Operator Overloading
Some languages have a neat feature that allows
you to overload the comparison operators. e.g. in
C++
people argue about
class Person { whether this is good
public: or bad.
Int mAge
bool operator==(Person &p) { (Java won’t let you do it)
return ([Link]==mAge);
};
}
Person a, b;
b == a; // Test value equality
183
Return Codes
The traditional imperative way to handle errors is to
return a value that indicates success/failure/error
if ( divide(x,y)<0) [Link](“Failure!!”);
Problems:
Could ignore the return value
Have to keep checking what the return values are meant to
signify, etc.
The actual result often can't be returned in the same way
Error handling code is mixed in with normal execution
184
Deferred Error Handling
A similar idea (with the same issues) is to set some state in
the system that needs to be checked for errors.
C++ does this for streams:
185
Exceptions
An exception is an object that can be thrown or raised by a
method when an error occurs and caught or handled by the
calling code
Example usage:
try {
double z = divide(x,y);
}
catch(DivideByZeroExcep9on d) {
// Handle error here
}
186
Flow Control During Exceptions
When an exception is thrown, any code left to run in the try
block is skipped
double z=0.0;
boolean failed=false;
try {
z = divide(5,0);
z = 1.0;
}
catch(DivideByZeroExcep9on d) {
failed=true;
}
z=3.0;
[Link](z+” “+failed);
187
Throwing Exceptions
An exception is an object that has Exception as an
ancestor
So you need to create it (with new) before throwing
188
Multiple Handlers
A try block can result in a range of different exceptions. We
test them in sequence
try {
FileReader fr = new FileReader(“someZle”);
Int r = [Link]();
}
catch(FileNoteFound fnf) {
// handle Zle not found with FileReader
}
catch(IOExcep9on d) {
// handle read() failed
}
189
finally
With resources we often want to ensure
that they are closed whatever happens
try {
fr,read();
[Link]();
}
catch(IOExcep9on ioe) {
// read() failed but we must s9ll close the FileReader
[Link]();
}
190
finally II
The finally block is added and will always
run (after any handler)
try {
[Link]();
}
catch(IOExcep9on ioe) {
// read() failed
}
Znally {
[Link]();
}
Remember try-with-resources
191
Creating Exceptions
Just extend Exception (or RuntimeException if you need it to
be unchecked). Good form to add a detail message in the
constructor but not required.
You can also add more data to the exception class to provide
more info on what happened (e.g. store the numerator and
denominator of a failed division)
Keyword: excep9on chaining
192
Exception Hierarchies
You can use inheritance hierarchies
194
Evil I: Exceptions for Flow Control
At some level, throwing an exception is like a GOTO
Tempting to exploit this
try {
for (int i=0; ; i++) {
[Link](myarray[i]);
}
}
catch (ArrayOutOfBoundsExcep9on ae) {
// This is expected
}
196
Evil II: Blank Handlers
Checked exceptions must be handled
Constantly having to use try...catch blocks to do this can be
annoying and the temptation is to just gaffer-tape it for now
try {
FileReader fr = new FileReader(Zlename);
}
catch (FileNotFound fnf) { If it can’t happen then throw
} a chained Run9meExcep9on
197
Advantages of Exceptions
Advantages:
Class name can be descriptive (no need to look up error
codes)
Doesn't interrupt the natural flow of the code by requiring
constant tests
The exception object itself can contain state that gives lots of
detail on the error that caused the exception
Can't be ignored, only handled
Disadvantages:
Surprising control flow – exceptions can be thrown from
anywhere
Lends itself to single threads of execution
Unrolls control flow, doesn’t unroll state changes
198
Remember the substitution principle?
If A extends B then I should be able to use
B everywhere I expect an A
} }
void process(A o) {
drawShape([Link]());
}
process(new B());
201
Covariant return types are substitutable
Overriding methods are covariant in their
return types
} } [Link]() returns
a Triangle but Triangle
is a subtype of Polygon
void process(A o) { and so by subs9tutability
drawShape([Link]()); we can pass it to
} drawShape
process(new B());
202
Contravariant parameters also substitute
Overriding methods can be contravariant
in their parameters
} } [Link]() wants a
Polygon and by
You can’t actually subs9tutability its ok
do this in Java! The void process(A o) { to pass it a Triangle
two setShapes are [Link](new Triangle());
overloads not }
overrides process(new B());
203
Java arrays are covariant
If B is a subtype of A then B[] is a subtype
of A[]
204
Imagine if Arrays were a generic class
205
Generics in Java are not covariant
if B is a subtype of A then T<B> is not a
subtype of T<A>
List<String> s = [Link](“v1”, “v2”);
206
Wildcards let us capture this
if B is a subtype of A then T<B> is a
subtype of T<? extends A>
List<String> s = [Link](“v1”, “v2”);
207
Inner classes
Inner classes may not have sta9c
class Outer { members
class Outer {
Method-local classes in
int y = 6;
instance methods can access
instance variables of the class
void f() {
int x = 5;
class Foo {
int g() { Method-local classes can
return x + y + 1; access local variables (and
} so are never sta9c classes).
}
Foo foo = new Foo();
209
Anonymous inner classes
210
Lambda
Predicate<Integer> b4 = v -> {
if (v > 0) {
return isPrime(v);
}
else { statement lambda
return isPrime(v*v);
}
}
boolean a = [Link](43431);
211
Need a Functional Interface to use them
A functional interface has only one method
in it
(this is so the compiler knows which one to
map the lambda on to)
That’s it
212
Streams
Collections can be made into streams
(sequences)
These can be filtered or mapped!
[Link]().map(x->x+10).collect([Link]());
[Link]().Zlter(x->x>5).collect([Link]());
216
The Open-Closed Principle
Classes should be open for extension
but closed for modification
217
Decorator
Abstract problem: How can we add state
or methods at runtime?
Demo: Readers
218
Decorator in General
The decorator
Reader
pattern adds
state and/or functionality to
an object dynamically
Bu(eredReader
FileReader
219
Singleton
Abstract problem: How can we ensure
only one instance of an object is created
by developers using our code?
222
State in General
The state pattern allows an
object to cleanly alter its
behaviour when internal
state changes
223
Strategy
Abstract problem: How can we select an
algorithm implementation at runtime?
Demo: ComparatorStrategy
224
Strategy in General
The strategy pattern allows us to cleanly interchange
between algorithm implementations
225
Composite
Abstract problem: How can we treat a
group of objects as a single object?
227
Observer
Abstract problem: When an object
changes state, how can any interested
parties know?
Demo: Ac9onListener
228
Observer in General
The observer pattern allows an object to have multiple
dependents and propagates updates to the dependents
automatically.
229