Week 5: Inheritance and Polymorphism II
Polymorphism, Interfaces, and Cellular Automata · 11.05.2026
Slides
Download Week 5 Slides (PDF) →
Code Examples
Three reference packages. The first is the W4 abstract Geometry hierarchy carried forward as the starter for Task 1 (add Drawable + Comparable). The second is the in-class Game of Life template you fill in during the lecture. The third is the cached-length PolyLine — the worked example for class invariants.
Code/Week5/src/at/ac/univie/gis/week5/
├── geometry/
│ ├── Geometry.java abstract base with label + abstract getArea()
│ ├── Circle.java concrete subclass — Task 1 adds Drawable + Comparable
│ └── Rectangle.java concrete subclass — Task 1 adds Drawable + Comparable
├── gameoflife/
│ └── GameOfLifeInClassTemplate.java 10×10 boolean[][], main loop wired; you fill in tick/neighbours/toString
└── polyline/
├── Point.java 2D point with distanceTo
├── PolyLine.java has-a ArrayList<Point>, cached length kept in sync by add()
└── Main.java small driver showing the cached length stays consistent
1. geometry/ — starter for Task 1 (interfaces on top of an abstract base)
Geometry.java — The same abstract base class from W4. Holds the shared label field and the abstract getArea() contract. Keep this file as it is — the parent type for shared state. The new capabilities (Drawable, Comparable) belong on the subclasses, as interfaces.
package at.ac.univie.gis.week5.geometry;
public abstract class Geometry {
private String label;
public Geometry(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
/**
* Abstract method - no body, just a contract.
* Each subclass fills this in with its own area formula.
*/
public abstract double getArea();
/**
* A concrete method on an abstract class is allowed.
* It can call the abstract method - the call resolves to the
* subclass implementation at runtime (dynamic dispatch).
*/
public String describe() {
return label + " has area " + getArea();
}
}
Circle.java & Rectangle.java — Two concrete subclasses, each with its own area formula. Task 1 asks you to add implements Drawable, Comparable<Geometry> here: extends Geometry stays for the shared state, and the two interfaces add the new capabilities. This is slide 13 in code form — single inheritance for state, multiple interfaces for capabilities.
package at.ac.univie.gis.week5.geometry;
public class Circle extends Geometry {
private double radius;
public Circle(String label, double radius) {
super(label);
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}
package at.ac.univie.gis.week5.geometry;
public class Rectangle extends Geometry {
private double width;
private double height;
public Rectangle(String label, double width, double height) {
super(label);
this.width = width;
this.height = height;
}
@Override
public double getArea() {
return width * height;
}
}
2. gameoflife/ — the in-class template
GameOfLifeInClassTemplate.java — A 10×10 boolean[][] seeded with a small starter pattern, plus a main loop that prints / ticks / sleeps. Three methods are stubbed for you to fill in. The point of the exercise is the copy-and-swap pattern in tick(): a fresh nextGame array is allocated, every cell’s next state is computed from the current game, then a single assignment commits the new generation. This is the same pattern Task 3 asks you to apply to the SIR step.
package at.ac.univie.gis.week5.gameoflife;
/**
* Conway's Game of Life - Template for in-class exercise.
*
* Rules (to implement in tick()):
* 1. Any live cell with fewer than 2 live neighbors dies (underpopulation).
* 2. Any live cell with 2 or 3 live neighbors survives.
* 3. Any live cell with more than 3 live neighbors dies (overpopulation).
* 4. Any dead cell with exactly 3 live neighbors becomes alive (reproduction).
*
* Students should implement: tick(), neighbours(), and toString().
*/
public class GameOfLifeInClassTemplate {
// The game grid: true = alive, false = dead
private boolean[][] game =
{
{false, false, false, false, false, false, false, false, false, false},
{false, false, false, true, true, false, false, false, false, false},
{false, false, false, true, true, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false},
{false, false, false, true, true, false, true, false, false, false},
{false, true, true, true, false, false, false, false, false, false},
{false, true, false, false, false, true, false, false, false, false},
{false, false, false, false, true, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false}
};
public GameOfLifeInClassTemplate() {
// Constructor - add initialization code if needed
}
/**
* Advances the simulation by one step, applying the Game of Life rules.
* IMPORTANT: All cells must be updated simultaneously, so we write to a new array
* and then replace the old one (otherwise earlier updates would affect later ones).
*/
public void tick() {
boolean[][] nextGame = new boolean[game.length][game[0].length];
// TODO: Apply the four rules using neighbours() to fill nextGame, then set game = nextGame
}
/**
* Counts the number of live neighbors around cell (i, j).
* Uses Queen's adjacency (8 surrounding cells). Must handle edge cases at grid boundaries.
*/
public int neighbours(int i, int j) {
int count = 0;
// TODO: Check all 8 surrounding cells, incrementing count for each live neighbor
return count;
}
/**
* Returns a string representation of the grid for console output.
* For example, use '#' for alive and '.' for dead cells.
*/
@Override
public String toString() {
// TODO: Build a string representation of the grid
return "";
}
/**
* Main loop: prints the grid, advances one tick, waits, and repeats forever.
*/
public static void main(String[] args) throws InterruptedException {
GameOfLifeInClassTemplate game1 = new GameOfLifeInClassTemplate();
while (true) {
System.out.println(game1);
game1.tick();
Thread.sleep(500); // Pause between frames (increase to 2000ms for debugging)
}
}
}
3. polyline/ — class invariants on a cached field
PolyLine.java — The ArrayList<Point> is private, and a cached length field is kept in sync by add(): the new segment’s distance is added in the same step as the point itself. Because the list is private, no outside code can append behind PolyLine’s back — that’s what lets the class promise “my stored length always equals the actual sum of segment lengths.” That promise is the class invariant; private fields plus a single update gate are what make it stick. computeLength() exists as a from-scratch fallback after bulk modifications; getLength() is a cheap field read.
package at.ac.univie.gis.week5.polyline;
import java.util.ArrayList;
public class PolyLine {
private ArrayList<Point> pointlist;
private double length = 0;
public PolyLine() {
pointlist = new ArrayList<Point>();
// Initialize length to ensure getLength() returns 0 even before any points are added
computeLength();
}
/**
* Adds a point to the polyline and updates the total length.
* This is why encapsulation matters: without this method, a user could add points
* directly to the list and getLength() would return a stale (incorrect) value.
*/
public boolean add(Point e) {
if (!pointlist.isEmpty()) {
// Add the distance from the last point to the new point
length += pointlist.get(pointlist.size() - 1).distanceTo(e);
}
return pointlist.add(e);
}
public Point get(int index) {
return pointlist.get(index);
}
public int size() {
return pointlist.size();
}
/**
* Recomputes the total length from scratch by summing distances between consecutive points.
* Normally not needed since add() keeps length up-to-date, but useful after modifications.
*/
public double computeLength() {
length = 0; // Reset before summing - the loop adds to length
for (int i = 0; i < size() - 1; i++) {
length += Math.sqrt(
Math.pow((get(i).getX() - get(i + 1).getX()), 2) +
Math.pow((get(i).getY() - get(i + 1).getY()), 2));
}
return length;
}
/**
* Returns the current total length of the polyline.
* Always up-to-date because add() updates it incrementally.
*/
public double getLength() {
return length;
}
}
Point.java — A plain 2D point with distanceTo. Same shape as the W3/W4 Point, lifted into the W5 package alongside PolyLine.
package at.ac.univie.gis.week5.polyline;
public class Point {
private double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Point{x=" + x + ", y=" + y + '}';
}
public double distanceTo(Point other) {
return Math.sqrt(Math.pow(x - other.getX(), 2) + Math.pow(y - other.getY(), 2));
}
public double getX() { return x; }
public void setX(double x) { this.x = x; }
public double getY() { return y; }
public void setY(double y) { this.y = y; }
}
Main.java — A small driver. Adds points one by one, then prints both getLength() (the cached value) and computeLength() (the from-scratch recomputation) so you can see the invariant holding: the two numbers agree.
package at.ac.univie.gis.week5.polyline;
public class Main {
public static void main(String[] args) {
// Create an empty polyline
PolyLine poly = new PolyLine();
System.out.println("Empty polyline's length: " + poly.getLength());
// A 1-point polyline has length 0 (no segments yet)
// Whether a 1-point polyline should be allowed is a design decision
poly.add(new Point(5, 5));
System.out.println("One-point polyline's length: " + poly.getLength());
// Adding more points creates line segments, increasing the total length
poly.add(new Point(6, 6));
poly.add(new Point(8, 7));
poly.add(new Point(9, 10));
// add() keeps the length up-to-date; computeLength() recalculates from scratch
// Both should return the same result
System.out.println("Polyline's length: " + poly.computeLength());
}
}
Assignment
Week 5 Assignment
Reading: Chapters on interfaces and 2D arrays.
Coding: Three small tasks — interface practice, finish the Game of Life template, fix the SIR step with copy-and-swap.
- Drawable + Comparable on Geometry. In
Code/Week5/geometry/, declare a smallDrawableinterface with one methodString draw()returning a one-line text representation. MakeCircleandRectangleimplement bothDrawableandComparable<Geometry>(compareToorders by area). In a smallmain, build aList<Geometry>mixing circles and rectangles, sort withCollections.sort(list), and print each viadraw(). This is the design rule from today’s lecture in code: one parent for shared state, multiple interfaces for capabilities. - Finish the Game of Life template. Open
gameoflife/GameOfLifeInClassTemplateand fill in the threeTODOmethods —tick()with copy-and-swap,neighbours()with edge guards (out-of-bounds neighbours count as dead), and atoString()using#for alive and.for dead. Test on the blinker. The 2D + copy-and-swap pattern you write here is the foundation for the W8–W10 mandatory SIR cellular-automaton assignment. - Fix the SIR step with copy-and-swap. Refactor your W4
Code/Week4/sir/Main.javaSIRstepso every person’s next state is computed from the population’s current state, then applied at the end. The cleanest shape is a parallelint[] nextStatesaligned withpopulation: compute next states for everyone first, then walk the array once and apply. Same pattern as Game of Life, just on a 1D list ofPersoninstead of a 2D grid.
Submission: Upload FirstNameLastNameW5.zip with your .java files
Deadline: Sunday at 5:00 PM (Vienna Time) — extended to right before Week 7 so you have time to prepare for the midterm.