0% found this document useful (0 votes)
111 views271 pages

Oop Slides

Uploaded by

Saifuddin Khan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
111 views271 pages

Oop Slides

Uploaded by

Saifuddin Khan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Practical work is on [Link].

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

Demo: Log into chime and opt-in/opt-out


14
We use git over SSH for version control
 Same setup as github and
[Link]
 Generate an SSH key
 Put the public part of the key on chime

Demo: creating an SSH key and adding it


to chime

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

Demo: starting a 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

Demo: cloning your task into a new project


18
Maven is a build system for Java
 In the pre-arrival course you built your
code manually
 This doesn’t scale well
 Use a build system!
 There are many build systems for Java
 All of them have strengths and weaknesses
 We will use Maven in this course

Demo: Maven pom file and build


19
Be careful about what you check in
 Imagine you are working in a team on a
shared code base
 Other engineers don’t want your IDE
settings
 Or your temp files
 Or your class files
 Or personal information!!!
 We use .gitignore to tell git to ignore some
files

20
Types of Languages

 Declarative - specify what to do, not how


to do it. i.e.
 E.g. HTML describes what should appear on a web page,
and not how it should be drawn to the screen
 E.g. SQL statements such as “select * from table” tell a
program to get information from a database, but not how to
do so

 Imperative – specify both what and how


 E.g. “triple x“ might be a declarative instruction that you
want the variable x tripled in value. Imperatively we would
have “x=x*3” or “x=x+x+x”

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.

let rec factorial n =


match n with
| 0 -> 1
| 1 -> 1
| n -> n * (factorial (n – 1)); 33
Function Side Effects
 Functions in imperative languages can use or
alter larger system state → procedures

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;
}

count+=1 count++ ++count

35
Control Flow: Looping

for( ini#alisa#on; termina#on; increment )

for (int i=0; i<8; i++) …

int j=0; for(; j<8; j++) …

for(int k=7;k>=0; j--) ...


Demo: prin9ng the numbers
from 1 to 10

while( boolean_expression )

int i=0; while (i<8) { i++; …}

int j=7; while (j>=0) { j--; ...}

36
Control Flow: Branching I
 Branching statements interrupt the current control flow
 return
 Used to return from a function at any point

boolean linearSearch(int[] xs, int v) {


for (int i=0;i<[Link]; i++) {
if (xs[i]==v) return true;
}
return false;
}

38
Control Flow: Branching II
 Branching statements interrupt the current control flow
 break
 Used to jump out of a loop

boolean linearSearch(int[] xs, int v) {


boolean found=false;
for (int i=0;i<[Link]; i++) {
if (xs[i]==v) {
found=true;
break; // stop looping
}
}
return found;
}

39
Control Flow: Branching III
 Branching statements interrupt the current control flow
 continue
 Used to skip the current iteration in a loop

void printPositives(int[] xs) {

for (int i=0;i<[Link]; i++) {


if (xs[i]<0) continue;
[Link](xs[i]);
}
}

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

Demo: int → byte overHow 43


Overloading Functions
 Same function name
 Different arguments
 Possibly different return type

int myfun(int a, int b) {…}


Hoat myfun(Hoat a, Hoat b) {…}
double myfun(double a, double b) {...}

 But not just a different return type

int myfun(int a, int b) {…}


Hoat myfun(int a, int b) {…} x
44
Function Prototypes
 Functions are made up of a prototype and
a body
 Prototype specifies the function name,
arguments and possibly return type
 Body is the actual function code

fun myfun(a,b) = …;

int myfun(int a, int b) {...}

45
Custom Types

type 'a seq =


| Nil
| Cons of 'a * (unit -> 'a seq);

public class Vector3D {


Hoat x;
Hoat y;
Hoat z;
}

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;

public class Vector3D {


Hoat x;
Hoat y; STATE
Hoat z;

void add(Hoat vx, Hoat vy, Hoat vz) {


x=x+vx;
y=y+vy;
BEHAVIOUR
z=z+vz;
}
} 48
Loose Terminology (again!)

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

public class Vector3D {


Hoat x;
Hoat y;
Hoat z;

void add(Hoat vx, Hoat vy, Hoat vz) {


x=x+vx;
y=y+vy;
z=z+vz;
}
}

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.

 It's a method that gets called when the object is constructed,


and it goes by the name of a constructor (it's not rocket
science). It maps to the datatype constructors you saw in ML.

 We use constructors to initialise the state of the class in a


convenient way
 A constructor has the same name as the class
 A constructor has no return type

52
Constructors with Arguments
public class Vector3D {
Hoat x;
Hoat y;
Hoat z;

Vector3D(*oat xi, *oat yi, *oat zi) {


x=xi;
y=yi;
z=zi; You can use ‘this’ to disambiguate names
} if needed: e.g. this.x = xi;

// ...
}

Vector3D v = new Vector3D(1.f,0.f,2.f);


53
Overloaded Constructors
public class Vector3D {
Hoat x;
Hoat y;
Hoat z;

Vector3D(*oat xi, *oat yi, *oat zi) {


x=xi;
y=yi;
z=zi;
}

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)

If you provide any constructor


then the default will not be
generated

 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

One of these created every


public class ShopItem {
Hoat mVATRate; 9me a new ShopItem is
sta9c Hoat sVATRate; instan9ated. Nothing keeps
.... them all in sync.
}

Only one of these created ever. Every


ShopItem object references it.

sta9c => associated with the class


instance => associated with the object

58
Class-Level Data and Functionality II
0.2
0.2  Shared between
0.2
instances
 Space efficient
17.5
0.2 17.5

instance Zeld sta9c Zeld sta9c Zelds are good for


(one per object) (one per class) constants. otherwise use
 Also static methods: with care.

public class Whatever {


public sta9c void main(String[] args) {
...
}
}
59
Why use Static Methods?
 Easier to debug (only depends on static state)
 Self documenting
 Groups related methods in a Class without requiring an object

public class Math { public class Math {


public Hoat sqrt(Hoat x) {…} public sta9c Hoat sqrt(Hoat x) {…}
public double sin(Hoat x) {…} public sta9c Hoat sin(Hoat x) {…}
public double cos(Hoat x) {…} public sta9c Hoat cos(Hoat x) {…}
} }
vs
… …
Math mathobject = new Math(); [Link](9.0);
[Link](9.0); ...
...

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

 We can do (much) better

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

“A quiz program that asks questions

and checks the answers are correct”

62
UML: Representing a Class Graphically

Ques9on

- prompt : String State


- solu9on: String
“-” means
private access + ask() : void
+ check(answer : String) : boolean Behaviour

“+” means
public access

63
The has-a Association
Quiz 1 0...* Ques9on

 Arrow going left to right says “a Quiz has zero or more


questions”
 Arrow going right to left says “a Question has exactly 1 Quiz”
 What it means in real terms is that the Quiz class will contain
a variable that somehow links to a set of Question objects,
and a Question will have a variable that references a Quiz
object.
 Note that we are only linking classes: we don't start drawing
arrows to primitive types.

Demo: implement quiz


64
Anatomy of an OOP Program (Java)
Class name
Access modiZer
public class MyFancyClass {
Class state (proper9es that an
public int someNumber; object has such as colour or size)
public String someText;

public void someMethod() { Class behaviour (ac9ons an


object can do)
}
'Magic' start point for
public sta9c void main(String[] args) { the program (named
MyFancyClass c = new main by conven9on)
MyFancyClass();
}

Create a reference to a Create an object of type


MyFancyClass object and call MyFancyClass in memory
it c 65
OOP Concepts
 OOP provides the programmer with a
number of important concepts:

 Modularity
 Code Re-Use
 Encapsulation
 Inheritance (lecture 5)
 Polymorphism (lecture 6)

 Let's look at these more closely...

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;

Student s2 = new Student();


[Link]=-1;

Student s3 = new Student();


[Link]=10055;
}
68
Encapsulation II
class Student {
private int age;

boolean setAge(int a) {
if (a>=0 && a<130) {
age=a;
return true;
}
return false;
}

int getAge() {return age;}


}

void main() {
Student s = new Student();
[Link](21);
} 69
Encapsulation III

class Loca9on { class Loca9on {


private Hoat x;
private Hoat y; private Vector2D v;

Hoat getX() {return x;} Hoat getX() {return [Link]();}


Hoat getY() {return y;} Hoat getY() {return [Link]();}

void setX(Hoat nx) {x=nx;} void setX(Hoat nx) {[Link](nx);}


void setY(Hoat ny) {y=ny;} void setY(Hoat ny) {[Link](ny);}
} }

Encapsula9on =
1) hiding internal state
2) bundling methods with state

70
Access Modifiers

Everyone Subclass Same Same


package Class
(Java)
private X

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

> fun self(x)=x; Fun fact: iden9ty is the only


val self = fn : 'a -> 'a func9on in ML with type ‘a → ‘a

 In Java, we can achieve something similar through Generics;


C++ through templates
 Classes are defined with placeholders (see later lectures)
 We fill them in when we create objects using them

LinkedList<Integer> = new LinkedList<Integer>()


LinkedList<Double> = new LinkedList<Double>()

73
Creating Parameterised Types
 These just require a placeholder type

class Vector3D<T> {
private T x;
private T y;

T getX() {return x;}


T getY() {return y;}

void setX(T nx) {x=nx;}


void setY(T ny) {y=ny;}
}

74
Generics use type-erasure
class Vector3D<T> { class Vector3D {
private T x; private Object x;
private T y; private Object y;

T getX() {return x;} Object getX() {return x;}


T getY() {return y;} Object getY() {return 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

Items on the heap exist until they are deleted

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

Return the value 1 and then


execute instruc9on 10
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 } 1 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 } 1 m
10 int m = sum(n - 1); 3 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 } 1 m
10 int m = sum(n - 1); 3 r
11 int r = m + n;
>> 12 return r;
13 }

Return the value 3 and then


execute instruc9on 10
1 static int sum() { sum()
s
2 int s = sum(3);
3 return s; sum(3) 2 return
4 } 3 n
5 3 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 3 m
6 static int sum(int n) { 6 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 3 m
6 static int sum(int n) { 6 r
7 if (n == 0) {
8 return 0;
9 }
10 int m = sum(n - 1);
11 int r = m + n;
>> 12 return r;
13 }

Return the value 6 and then


execute instruc9on 2
1 static int sum() { sum()
>> 2 6 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()
6 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 }

Return the value 6 and then


execute whatever called us
In Java primitive types go on the stack

Everything else goes on the heap

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 }

Java delete’s for us


automatically. This is
called Garbage Collection

<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 }

‘a’ and ‘s’ are references. These are like


pointers but you can’t do arithmetic
on them.

When you say [Link]() you are ‘dereferencing’


s and calling the method toUpperCase on it.

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 }

This example is in C++


20
1 static void test() { 3 i
2 int i = 3; <addr> k
>> 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 }

& on the LHS means


‘reference’

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

Can be unassigned Yes Yes


(null)
Can be assigned to Yes Yes
established object
Can be assigned to an Yes No
arbitrary chunk of
memory
Can be tested for validity No Yes

Can perform arithmetic Yes No

110
References in Java
 Declaring unassigned
SomeClass ref = null; // explicit

SomeClass ref2; // implicit

 Defining/assigning
// Assign
SomeClass ref = new ClassRef();

// Reassign to alias something else


ref = new ClassRef();

// Reference the same thing as another reference


SomeClass ref2 = ref;
111
Argument Passing
 Pass-by-value. Copy the value into a new one in
the stack
void test(int x) {...}
int y=3;
test(y);

void test(Object o) {…}


Object p = new Object();
test(p);

The value passed here is the


reference to the object.

When run the test method’s


argument o is copy of the reference
p that points to the same object

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

Demo: expression evaluator

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

Note: Java is a nomina*ve type language (rather than a structurally


typed one)

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

name As in “Student is-a Person”


age

Specialise
Generalise

Student Lecturer

exam_score
salary

name and age


inherited if not
private
119
Casting
 Many languages support type casting
between numeric types

int i = 7;
Hoat f = (Hoat) i; // f==7.0
double d = 3.2;
int i2 = (int) d; // i2==3

 With inheritance it is reasonable to type


cast an object to any of the types above it
in the inheritance tree...

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 s = new Student() public void print(Person p) {...}

Person p = s; Student s = new Student();


print(s);

Implicit widening 121


Narrowing
Person  Narrowing conversions move down
the tree (more specific)
 Need to take care...

Student
OK because underlying object
really is a Student

Person p = new Person(); public void print(Person p) {


Student s = (Student) p;
Student s = (Student) p; }

Student s = new Student();


FAILS at run9me. Not enough info
print(s);
In the real object to represent
a Student 122
Fields and Inheritance

class Person { Student inherits this as a public


public String name; variable and so can access it
protected int age;
private double height;
} Student inherits this as a
protected variable and so can
class Student extends Person {
access it
public void do_something() {
name=”Bob”;
age=70; Student inherits this but as a
height=1.70; private variable and so cannot
} access it directly
This line doesn’t compile
}

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()
}
}

class Student extends Person {


public void dance() { Student overrides the
original
body_pop();
}
}
Lecturer overloads the Lecturer inherits the
inherited dance() class Lecturer extends Person { original implementa9on
method public void dance(int dura9on) {...} and jiggles
} 125
Abstract Methods
 Sometimes we want to force a class to implement a method
but there isn't a convenient default behaviour
 An abstract method is used in a base class to do this
 It has no implementation whatsoever

class abstract Person {


public abstract void dance();
}

class Student extends Person {


public void dance() {
body_pop();
}
}

class Lecturer extends Person {


public void dance() {
jiggle_a_bit();
}
}
126
Abstract Classes
 Note that I had to declare the class abstract too. This is
because it has a method without an implementation so
we can't directly instantiate a Person.

public abstract class Person {


public abstract void dance();
}

 All state and non-abstract methods are inherited as


normal by children of our abstract class
 Interestingly, Java allows a class to be declared abstract
even if it contains no abstract methods!

127
Representing Abstract Classes
Person

Italics indicate the class


+ dance() or method is abstract

Student Lecturer

+ dance() + dance()

128
Polymorphic Methods

Student s = new Student();  Assuming Person has a


Person p = (Person)s; dance() method, what should
[Link](); happen here?

Demo: revisit expressions from last 9me

 General problem: when we refer to an object via a parent


type and both types implement a particular method: which
method should it run?

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

Student s = new Student();  Compiler says “p is of type Person”


Person p = (Person)s;  So [Link]() should do the default
[Link](); dance() ac9on in Person

C++ can do this. Java cannot


132
Polymorphic Concepts II
 Dynamic polymorphism
 Run the method in the child
 Must be done at run-time since that's when we
know the child's type
 Also known as ‘dynamic dispatch’

Student s = new Student();  Compiler looks in memory and Znds


Person p = (Person)s; that the object is really a Student
[Link]();  So [Link]() runs the dance() ac9on
in Student

C++ can do this when you choose, Java does it always


133
The Canonical Example I
 A drawing program that can draw circles,
squares, ovals and stars
 It would presumably keep a list of all the
drawing objects
 Option 1
Circle  Keep a list of Circle objects, a list of
+ draw()
Square objects,...
Square  Iterate over each list drawing each
+ draw() object in turn
 What has to change if we want to add
Oval a new shape?
+ draw()

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()

For every Shape s in myShapeList


Square [Link]();
+ draw()

 What if we want to add a new shape?


Oval
+ 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

 Polymorphism in OOP is an extremely important concept


that you need to make sure you understand...

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 {…}

class BlobFish : public Fish,


public 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++:

BlobFish *bf = new BlobFish();


bf->Fish::move();
bf->DrawableEn9ty::move();
 Yuk.
BlobFish
This is like Zeld shadowing e.g.
????
class A { class B extends A {
int x; int x;
} }
140
Multiple Inheritance Problems
CountableEn9ty CountableEn9ty  What happens if Fish and
DrawableEntity extend the same
+ freq: int + freq: int
class?
 Do I get two copies?

Fish DrawableEn9ty

+ move() + move()

BlobFish

????
141
The diamond problem
CountableEn9ty CountableEn9ty CountableEn9ty

+ freq: int + freq: int + freq: int

Fish DrawableEn9ty DrawableEn9ty


Fish

+ move() + move() + move() + move()


or

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)
}

class Bicycle implements Drivable {


public void turn() {...}
public void brake() {… }
Bicycle Car }
+ turn() + turn()
+ brake() class Car implements Drivable, Iden9Zable {
+ brake()
+ getIden9Zer() public void turn() {...}
public void brake() {… }
public void getIden9Zer() {...}
}
144
Interfaces have a load of implicit modifiers

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;
}
}

 Allows you to add new functionality without


breaking old code
 If you implement conflicting default methods
you have to provide your own
146
Creating Objects in Java
new MyObject()

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 3. Call Student()


151
Chaining without Default Constructors
 What if your classes have explicit constructors that take
arguments? You need to explicitly chain
 Use super in Java:

public Person (String name) {


Person
mName=name;
-mName : String
+Person(String name) }

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

class FileReader { int main(int argc, char ** argv) {


public:
FileReader f;
// Constructor
FileReader() { // Use object here
f = fopen(“myZle”,”r”); ...
C++ }
} // object destructor called here
// Destructor
~FileReader() {
fclose(f);
}

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

Genera9onal garbage collec9on: split


objects into short-lived and long-lived and
object
collect short-lived more frequently
Unreachable
so deleted
158
Boxing and unboxing
 Boxing: turn an int into an Integer
 Unboxing: turn an Integer into an int
 Java will do auto-boxing and unboxing
public void something(Integer I) {
...
}
auto-boxing
int i = 4;
something(i);
public void other(int i) {

}
auto-unboxing
(and a NPE) Integer i = null;
other(i);

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”)

 The Collections framework has two main


interfaces: Iterable and Collection. They define
a set of operations that all classes in the
Collections framework support
 add(Object o), clear(), isEmpty(), etc.

Some9mes an opera9on doesn’t make sense – throw UnsupportedOpera9onError


163
Sets
<<interface>> Set
 A collection of elements with no duplicates that
represents the mathematical notion of a set B
A
 TreeSet: objects stored in order C
 HashSet: objects in unpredictable order but fast
to operate on (see Algorithms course) Iterable

Set<Integer> ts = new TreeSet<>(); Collec9on


[Link](15);
[Link](12); Set
[Link](7); // false
[Link](12); // true SortedSet HashSet LinkedHashSet
[Link](); // 12 (sorted)
TreeSet

A form of type inference

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)

Map<String, Integer> tm = new TreeMap<String,Integer>();


[Link](“A”,1);
[Link](“B”,2);
[Link](“A”); // returns 1
[Link](“C”); // returns null
[Link](“G”); // false
167
Don’t just memorise these – think about how the datastructure works
168
Specific return type and general argument
 Should your method take a Set, a
SortedSet or a TreeSet?
 General rule of thumb:
 use the most general type possible for
parameters
 use the most specific type possible for return
values (without over committing your
implementation)

169
Iteration
 for loop
LinkedList<Integer> list = new LinkedList<Integer>();
...
for (int i=0; i<[Link](); i++) {
Integer next = [Link](i);
}

 foreach loop (Java 5.0+)

LinkedList list = new LinkedList();


...
for (Integer i : list) {
...
}
170
Iterators
 What if our loop changes the structure?
for (int i=0; i<[Link](); i++) {
If (i==3) [Link](i);
}
 Java introduced the Iterator class
Iterator<Integer> it = [Link]();

while([Link]()) {Integer i = [Link]();}

for (; [Link](); ) {Integer i = [Link]();}

 Safe to modify structure


while([Link]()) {
[Link]();
}
171
Demo: Fast fail behaviour
Comparing Objects
 You often want to impose orderings on your
data collections
 For TreeSet and TreeMap this is automatic
TreeMap<String, Person> tm = ...
 For other collections you may need to explicitly
sort

LinkedList<Person> list = new LinkedList<Person>();


//...
[Link](list);
 For numeric types, no problem, but how do you
tell Java how to sort Person objects, or any
other custom class?
172
Comparing Primitives
> Greater Than
>= Greater than or equal to
== Equal to
!= Not equal to
< Less than
<= Less than or equal to

 Clearly compare the value of a primitive


 But what does (ref1==ref2) do??
 Test whether they point to the same object?
 Test whether the objects they point to have the same
state?
175
Reference Equality
 r1==r2, r1!=r2
 These test reference equality
 i.e. do the two references point ot the same chunk of
memory?
Person p1 = new Person(“Bob”);
Person p2 = new Person(“Bob”);
False (references di(er)
(p1==p2);

(p1!=p2); True (references di(er)

(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);
}

public sta9c void main(String args[]) {


EqualsTest t1 = new EqualsTest();
EqualsTest t2 = new EqualsTest();
[Link](t1==t2);
[Link]([Link](t2));
}
}
177
Demo: What’s wrong with equals
Java Quirk: hashCode()
 Object also gives classes hashCode()
 Code assumes that if equals(a,b) returns
true, then [Link]() is the same as
[Link]()
 So you should override hashCode() at the
same time as equals()

Learn the ‘hashcode’ contract

178
Comparable<T> Interface I
int compareTo(T obj);

 Part of the Collections Framework


 Doesn't just tell us true or false, but smaller, same, or
larger: useful for sorting.
 Returns an integer, r:
 r<0 This object is less than obj
 r==0 This object is equal to obj
 r>0 This object is greater than 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.
}
}
}

// This will be sorted automa9cally by y, then x


Set<Point> list = new TreeSet<Point>();
Demo 180
Comparator<T> Interface I
int compare(T obj1, T obj2)

 Also part of the Collections framework and allows us


to specify a specific ordering for a particular job
 E.g. a Person might have natural ordering that sorts
by surname. A Comparator could be written to sort
by age instead...

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
}
}

public class AgeComparator implements Comparator<Person> {


public int compare(Person p1, Person p2) {
return ([Link]);
}
}


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

public int divide(double a, double b) {


if (b==0.0) return -1; // error Go – returns a pair res, err
double result = a/b; Haskell – Maybe type
return 0; // success
}

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:

ifstream Zle( "[Link]" );


if ( [Link]() )
{
cout << "An error occurred opening the Zle" << endl;
}

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

double divide(double x, double y) throws DivideByZeroExcep9on {


if (y==0.0) throw new DivideByZeroExcep9on();
else return x/y;
}

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.

public class DivideByZero extends Excep9on {}

public class Computa9onFailed extends Excep9on {


public Computa9onFailed(String msg) {
super(msg);
If your excep9on is caused
} by another then chain
} them - demo

 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

public class MathExcep9on extends Excep9on {...}


public class InZniteResult extends MathExcep9on {…}
public class DivByZero extends MathExcep9on {…}

 And catch parent classes


try {

}
catch(InZniteResult ir) {
// handle an inZnite result
}
catch(MathExcep9on me) {
// handle any MathExcep9on or DivByZero
}
193
Checked vs Unchecked Exceptions
 Checked: must be handled or passed up.
 Used for recoverable errors
 Java requires you to declare checked exceptions that your
method throws
 Java requires you to catch the exception when you call the
function

double somefunc() throws SomeExcep*on {}

 Unchecked: not expected to be handled. Used for


programming errors
 Extends RuntimeException
 Good example is NullPointerException

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
}

 This is not good. Exceptions are for exceptional circumstances


only
 Harder to read
 May prevent optimisations

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

 ...but we never remember to fix it and we could easily be missing


serious errors that manifest as bugs later on that are extremely
hard to track down

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

class A { class B extends A {

Polygon getShape() { Polygon getShape() {


return new Polygon(…); return …
} }

} }

void process(A o) {
drawShape([Link]());
}
process(new B());
201
Covariant return types are substitutable
 Overriding methods are covariant in their
return types

class A { class B extends A {

Polygon getShape() { Triangle getShape() {


return new Polygon(…); return …
} }

} } [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

class A { class B extends A {

void setShape(Triangle o) { void setShape(Polygon o) {


… …
} }

} } [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[]

String[] s = new String[] { “v1”, “v2” };

Object[] t = s; Compiles – arrays are covariant


Works – t[0] is actually a String
Object v = t[0];
but we can assign that to Object
t[1] = new Integer(4); Fails (at run9me) – t[] is actually
an array of Strings, you can’t
put an Integer in it

204
Imagine if Arrays were a generic class

class Array<Object> { class Array<String> {

// Object x = array[i] // String x = array[i]


Object get(int index) { String get(int index) {
… Covariant return type – all is good!
} }

// array[i] = value // array[i] = value


void set(int index, void set(int index,
Object value) { String value) {
… Covariant parameter type – bad news
} }
} }

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”);

List<Object> t = s; Does not compile

Object v = [Link](0); Would be safe – we can


assign String to Object
[Link](1,new Integer(4)); Is not safe

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”);

List<? extends Object> t = s; Compiles


Works: ‘? extends Object’
Object v = [Link](0);
is assignable to Object
[Link](1,new Integer(4));

Does not compile – the compiler knows it needs


something that extends object but it doesn’t
know what it is!

207
Inner classes
Inner classes may not have sta9c
class Outer { members

private static void f();


private int x = 4;
Sta9c inner classes are a member
static class StaticInner { of the outer class and so can
access private members
void g() {
f();
new Outer().x = 3;
}
}

class InstanceInner { Instance inner classes are a member


int g() { of the outer object and so can access
return x + 1; outer instance variables:
}
} Outer o = new Outer();
InstanceInner i = [Link] InstanceInner()
} 208
Method-local classes

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

class Outer { x here is ‘e(ec9vely Znal’ - compile


error if you try to change it
int y = 6;

Object f() { o is a new class. It extends


int x = 5; Object but it has no name.
Object o = new Object() { It can access all local and
public String toString() { instance variables.
return [Link](x+y+1);
}
};
return o;
}
} Note: here we return o to the caller and it can be
used anywhere in the program even though it refers
to y and x.

210
Lambda

Consumer<String> c1 = s -> [Link](s);


[Link](“hello”);
expression lambda

BiFunction<Integer,Integer,Boolean> c2 = (i,j) -> i+j > 5;


boolean a = [Link](3,1);

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!

List<Integer> list = ...

[Link]().map(x->x+10).collect([Link]());

[Link]().Zlter(x->x>5).collect([Link]());

create element-wise aggrega9on


stream opera9ons
Demo:streams
215
Design Patterns
 A Design Pattern is a general reusable solution to a
commonly occurring problem in software design
 Coined by Erich Gamma in his 1991 Ph.D. thesis
 Originally 23 patterns, now many more. Useful to look at
because they illustrate some of the power of OOP (and
also some of the pitfalls)
 We will only consider a subset
 It’s not a competition to see how many you can use in a
project!

216
The Open-Closed Principle
Classes should be open for extension
but closed for modification

 i.e. we would like to be able to modify the


behaviour without touching its source code
 This rule-of-thumb leads to more reliable
large software and will help us to evaluate
the various design patterns

217
Decorator
Abstract problem: How can we add state
or methods at runtime?

Example problem: How can we efficiently


support gift-wrapped books in an online
bookstore?

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?

Example problem: You have a class that


encapsulates accessing a database over a
network. When instantiated, the object will
create a connection and send the query.
Unfortunately you are only allowed one
connection at a time.
220
demo: SingletonConnec9on
Singleton in General
 The singleton pattern ensures
a class has only one instance
and provides global access to
it

Demo: FanSpeed 221


State
Abstract problem: How can we let an
object alter its behaviour when its internal
state changes?

Example problem: Representing


academics as they progress through the
rank

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?

Example problem: We have many possible


change-making implementations. How do
we cleanly change between them?

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?

Example problem: Representing a DVD


box-set as well as the individual films
without duplicating info and with a 10%
discount

Demo: DVDs 226


Composite in General
 The composite pattern lets
us treat objects and groups
of objects uniformly

227
Observer
Abstract problem: When an object
changes state, how can any interested
parties know?

Example problem: How can we write


phone apps that react to accelerator
events?

Demo: Ac9onListener
228
Observer in General
 The observer pattern allows an object to have multiple
dependents and propagates updates to the dependents
automatically.

229

You might also like