Key Principles
Abstraction: data and functional abstraction Encapsulation: packaging and information hiding Inheritance: DRY principle Polymorphism
Point Object (location in 2D space)
Data Abstraction
double x
double y
List<Double>
Functional Abstraction
p.distanceTo(q)
Packaging
lower-level data
lower-level functionality
Writing classes (likely 10-20)
- Don’t need public
- Minimise the use of comments when you submit code
- Write minimal code
Opening new classes into jshell
/open Circle.java
/list Circle #checking if it exists
new Circle(new Point(0.0, 0.0), 1.0).contains9new Point(1.0, 1.0))
Has-A Relationship The circle class has dependencies (point, radius). You need to compile these foundations first.
Class: Point
class Point {
final double x;
final double y;
Point(double x, double y) {
this.x = x;
this.y = y;
}
double distanceTo(Point other) {
return Math.sqrt(Math.pow(other.x - this.x, 2) + Math.pow(other.y - this.y, 2));
}
}
Class: Circle
class Circle {
final Point centre; // Circle has a Point (has-a relationship)
final double radius;// Circle has a radius
Circle(Point centre, double radius) {
this.centre = centre;
this.radius = radius;
}
boolean contains(Point point) {
return this.centre.distanceTo(point) < this.radius;
}
}
Abstraction
Information Hiding
boolean contains(point, point) {
return this.center.distanceTo(point) <= this.radius;
}
boolean contains(Point point) {
return Math.sqrt(Math.pow(point.x - this.center.x, 2) + ...)
}
# We want to avoid this.
# For clients, when we pass them code, it should be guaranteed to work.
# What if they changed some part of the code in the second one?
# We don't want them to have access to *class properties*
# Plus, what if we changed implementation?
# Hence we use INFORMATION HIDING
# 1) using 'final' in 'final double x' -> you can't change
# 2) using 'private' in 'private final double x' -> things outside the class cannot use it
# 3) probably not write the code using class properties
Tell-Don’t-Ask
Tell an object what to do, don’t ask an object for data Minimize client access to accessor/getter, eg. point.x()
# avoid
private double getX() {
return x;
}
# can be useful
Avoid Cyclic Dependencies
You won’t be able to identify which is the most rudimentary class.

Class: FilledCircle (Inheritance)
It’s similar to Circle. DRY - Don’t Repeat Yourself principle. Avoid duplicates ⇒ Use Inheritance (is-a relationship) FilledCircle is an additional layer on the Circle. (extends) You can only inherit from ONE parent. (Incase you have multiple same functions)
import java.awt.Color;
class FilledCircle extends Circle {
private final Color color;
FilledCircle(Point centre,
double radius, Color color) {
super(centre, radius);
this.color = color;
}
...
# super. - To use a parent's method
FilledCircle fillColor(Color newcolor) {
return new FilledCircle(super.center, super.radius, newcolor);
}
# To make these 'super.center' work and actually access from the parent,
# we need to use protected instead of private in the parent class.
# Method Overriding - To redefine a parent's method
}
# Once you inherit from parent/super class, you have their functions too!
# i.e. don't need to define *boolean contains(Point point)...*
# You can add custom functions.
class Circle {
protected final Point center;
protected final double radius;
...
}
Convention is to use S (child) and T (parent).
Method: toString
Useful to represent useful information in the jshell.
class Point {
...
@Override
public String toString() {
return "(" + this.x + ", " + this.y + ")";
}
}
class Circle {
...
@Override
public String toString() {
return "circle at " + this.centre + " with radius " + this.radius;
}
}
class FilledCircle {
...
@Override
public String toString() {
return "filled " + super.toString() + ", " + this.color;
}
}
If I don't override, then I'll just end up with the same result.
# @Override is used by the compiler to check that the method overrides the one in the parent class.
Method Overloading
You can create methods of the same name if their method signatures (number, type, order of parameters) are different.
class Circle {
...
boolean contains(Point p) {
return this.centre.distanceTo(p) < this.radius;
}
boolean contains(Circle c) {
return this.centre.distanceTo(c.centre) + c.radius < this.radius;
}
}
Constructor Overloading
This is what happens when the parent’s properties must be private. So, we create a constructor in the parent’s class.
# In the parent Circle class:
Circle(Circle circle) {
this(circle.center, circle.radius)
this.center = circle.center;
this.radius = circle.radius;
}
# In the FilledCircle class:
FilledCircle(FilledCircle fc, Color color) {
super(fc);
this.color = color;
}
FilledCircle fillColor(Color newcolor) {
return new FilledCircle(this, color);