Week 8 of 8 · Java

Abstraction, Interfaces, and the Final

The shape of the Christian journey.

Chapter 8 — Abstraction, Interfaces, and the Final

“For now we see in a mirror dimly, but then face to face.” — 1 Corinthians 13:12

“I have fought the good fight, I have finished the race, I have kept the faith.” — 2 Timothy 4:7

“The bar is the same as last week. You’re different now.” — every coach the morning of the final fight

This week merges Coding 1 chapters 15 and 16. If you also own the sixteen-week book, this chapter is “Abstract Classes and Interfaces” and “Final Review” welded into one week — the last new material in the course, plus the cumulative review that gets you through the final.


Your Week at a Glance

Twelve honest hours, four sessions. This week is unusual: two of the sessions are learning, and two of them are the exam. Session 4 is the longest single block in the course. Protect it.

SessionHoursWhat you doCheckpoint at the end
1~3Read §8.1–§8.3. Type Shape.java, Circle.java, Rectangle.java, ShapeDemo.java from scratch and run them. Do Reps 1–5 in the exercises.You can write an abstract class with one abstract and one concrete method, and say out loud why new Shape() won’t compile.
2~3Read §8.4–§8.9. Build the six-file BattleDemo. Type Restorable, Witness, and their demos. Finish the reps. Take the §8.17 Checkpoint.You pass the Checkpoint at 6 of 8 or better. If not, re-drill §8.4–§8.5 before touching the exam.
3~2Read §8.10–§8.14 — the bridge, the eight-week compression, the patterns, the worked review problem. Run ReviewWorkout.java. Then take Final Part A in Canvas.Part A submitted. It’s auto-graded, so you know your score immediately.
4~4Final Part B — Pilgrim’s Journey Engine. One sitting if you can manage it. Compile early and often, write the authorship block, submit the link.Part B submitted before the window closes. Course complete.

Total: 12 hours. Sessions 1 and 2 are ordinary weeks. Session 3 is short. Session 4 is a four-hour block where you write a real program alone with the AI off, and it will feel long. Do not schedule it at the end of a workday, and do not schedule it the hour before the deadline.

Coach’s Note — The order that saves you is §8.1–§8.9 → the Checkpoint → Part A → Part B. Do not skip ahead to Part B because it’s worth more points. Part B is built out of the things §8.1–§8.9 teaches; skipping the material to save time on the material is how students hand in a file that doesn’t compile.


Why This Matters

Two ideas close out object-oriented programming: abstract classes — which you already met in C++ as pure virtual classes back in Chapter 6 — and interfaces, which are Java-specific and turn out to be one of the most powerful features in the language.

Together they let you specify what a type must do without specifying how — the bridge between design and implementation. Up to now, every class you wrote was a thing that worked. This week you learn to write the shape of a thing that will work, as a promise the compiler enforces, and then fill the shape in.

For the apologetics theme: this is where the formal architecture catches up with the lived Christian intellectual life. Theology has long worked in interfaces. The medieval scholastics talked about qua this and qua that — meaning “considered as.” A person qua sinner is one thing; qua image-bearer of God is another. A statement qua historical claim is one thing; qua doctrinal claim is another. A Java interface is exactly that move: marking what an object is, considered as a Healable thing, separately from what it is considered as a Combatant.

And then, in the back half of this chapter, the whole course comes back. Not as nostalgia — as a working reference you can scan the hour before you open the exam.


8.1 — Abstract Classes in Java

In C++ you made a class abstract by declaring at least one pure virtual method (= 0). Java is more explicit: you mark the class itself abstract, and you mark each unimplemented method abstract.

public abstract class Combatant {
    protected String name;
    protected int hp;
    protected int maxHp;

    public Combatant(String name, int hp) {
        this.name = name;
        this.hp = hp;
        this.maxHp = hp;
    }

    public abstract void takeTurn(Combatant opponent);   // no body — abstract

    public String getName() { return name; }
    public int getHp() { return hp; }
    public boolean isAlive() { return hp > 0; }

    public void receiveDamage(int amount) {
        hp = Math.max(0, hp - amount);
    }
}

Four things to notice, because each is a rule:

  • public abstract class Combatant — the class itself is abstract. You cannot instantiate it.
  • public abstract void takeTurn(Combatant opponent); — an abstract method. No body, no braces; the signature ends in a semicolon.
  • The other methods are ordinary. An abstract class can be partly implemented — some abstract methods, some finished ones. That is the whole point of it.
  • Abstract classes have fields and constructors, like any class. Combatant’s constructor runs; it just runs on behalf of a subclass.

A subclass must implement every abstract method it inherits. Here is Hero, abbreviated — the Healable half of the contract arrives in §8.4, so this excerpt on its own would not compile; the finished file is code/Hero.java:

public class Hero extends Combatant implements Healable {
    public Hero(String name, int hp) { super(name, hp); }

    @Override
    public void takeTurn(Combatant opponent) {
        System.out.println(name + " strikes " + opponent.getName() + " for 15.");
        opponent.receiveDamage(15);
    }
    // ... plus the Healable methods; §8.4
}

Hero is now concrete — every hole is filled, so you can new it.

A subclass that does not fill every hole is itself abstract, and Java makes you say so:

public abstract class Spellcaster extends Combatant {
    protected int mana;

    public Spellcaster(String name, int hp, int mana) {
        super(name, hp);
        this.mana = mana;
    }

    // Adds state and a helper, but never implements takeTurn(),
    // so Spellcaster stays abstract. Its subclasses must finish the job.
    public boolean canCast(int cost) { return mana >= cost; }
}

An abstract class in the middle of a hierarchy, adding shared machinery on the way down, is a normal design: CombatantSpellcaster → some concrete caster, with only the last link in the chain instantiable. (The Wizard in code/ skips the middle layer and extends Combatant directly — this snippet is showing you the option, not the file.)

Coach’s Note — Reach for an abstract class when the shared structure is obvious and instantiating the parent is meaningless. Ask it out loud: “What would it mean to make a generic Combatant?” Nothing. Every combatant has to be some kind of combatant. There’s your answer.


8.2 — Why You Cannot Instantiate an Abstract Type

Try it and the compiler stops you cold:

Shape s = new Shape();     // ❌
B1.java:3: error: Shape is abstract; cannot be instantiated
        Shape s = new Shape();
                  ^
1 error

The reason is mechanical, not bureaucratic. new Shape() would produce an object whose method table has a hole in it. Someone would eventually call s.area(), and there would be no code to run. Java refuses to build an object it cannot promise to operate; C++ refuses for the same reason and words it differently.

Three follow-on facts that trip people up:

1. The abstract class still has a constructor, and it still runs. super(name, hp) in Hero’s constructor initializes name, hp, and maxHp on the Hero object. Abstract does not mean “no constructor”; it means “not directly constructible.”

2. You can absolutely declare variables of the abstract type. This is legal and is the entire point:

Shape s = new Circle(3.0);            // ✅ abstract type, concrete object
Shape[] shapes = { new Circle(3.0), new Rectangle(4.0, 5.0) };   // ✅
ArrayList<Combatant> party = new ArrayList<>();                  // ✅

The variable is abstract-typed; the object it points at is always concrete. Same move you made in Chapter 7 with base-class references, now with a base that couldn’t be instantiated even if you wanted it.

3. Forget one abstract method and Java names it for youerror: Triangle is not abstract and does not override abstract method area() in Shape. That is the single most common Week 8 error, and the message is a to-do list: it tells you the exact method you still owe. §8.15 has the full text and the fix.


8.3 — The Partially-Implemented Base

Here is the move that makes abstract classes worth more than “inheritance with a warning label.” A concrete method in the base may call abstract methods that do not exist yet.

code/Shape.java:

public abstract class Shape {
    public abstract double area();
    public abstract String kind();

    public void describe() {
        System.out.println(kind() + " area = " + area());
    }
}

describe() is finished — real, compiled code — and it calls two methods that have no bodies anywhere in this file. That is legal because by the time describe() actually runs, it is running on a concrete object, and that object has both.

The base owns the shape of the answer. The subclass owns the two blanks:

public class Circle extends Shape {
    private double radius;
    public Circle(double radius) { this.radius = radius; }

    @Override public double area() { return Math.PI * radius * radius; }
    @Override public String kind() { return "circle"; }
}

Run code/ShapeDemo.java:

public class ShapeDemo {
    public static double totalArea(Shape[] shapes) {
        double total = 0.0;
        for (Shape s : shapes) {
            total += s.area();
        }
        return total;
    }

    public static void main(String[] args) {
        Shape[] shapes = { new Circle(3.0), new Rectangle(4.0, 5.0) };
        for (Shape s : shapes) {
            s.describe();
        }
        System.out.println("total area = " + totalArea(shapes));
    }
}

Actual output:

circle area = 28.274333882308138
rectangle area = 20.0
total area = 48.27433388230814

(The long decimals are just double being honest about π. Nothing is wrong.)

This pattern has a name in the trade — the template method. The base writes the algorithm; the subclasses supply the steps. It scales past one-liners: an abstract StudySession could have a concrete run() that calls warmUp(), mainSet(), and coolDown() in that fixed order, with warmUp() and mainSet() abstract and coolDown() given a sensible default body. Every subclass then inherits the same three-phase structure and only decides what happens inside each phase — and no subclass can scramble the order. That mix, some steps mandatory and some optional with the sequence locked, is the strongest argument for abstract classes over interfaces.


8.4 — Interfaces: The Pure Contract

An interface is a set of method signatures that any implementing class must provide. No instance fields. No constructors. (Historically, no method bodies at all — §8.7 covers the modern exception.) It is pure specification.

code/Healable.java:

public interface Healable {
    void heal(int amount);
    boolean canBeHealed();
}

Read it out loud: “A Healable thing must provide a heal(int) and a canBeHealed().” That is the entire interface. Note there is no public on the methods — interface methods are public automatically.

A class signs the contract with implements:

public class Hero extends Combatant implements Healable {
    public Hero(String name, int hp) { super(name, hp); }

    @Override
    public void takeTurn(Combatant opponent) { /* strike; see §8.1 */ }

    @Override
    public void heal(int amount) {
        hp = Math.min(maxHp, hp + amount);
        System.out.println(name + " heals " + amount + ". HP: " + hp);
    }

    @Override
    public boolean canBeHealed() { return hp < maxHp; }
}

Hero now claims two things at once: it is a Combatant (extends) and it can be healed (implements).

And now the payoff — polymorphism through an interface type:

public static void healAll(ArrayList<Healable> patients) {
    for (Healable p : patients) {
        if (p.canBeHealed()) {
            p.heal(10);
        }
    }
}

Look hard at that loop. It does not know whether each Healable is a Hero, a Wizard, a sapling, or a chapel roof, and it does not know whether they share a base class — they need not. It knows two method names, because those are the only two things the contract guarantees. That is “program to the interface, not the implementation,” in its strongest form.

Two rules that come straight out of “no instance fields”:

  • An interface cannot hold state. No private int hp;. If your capability needs remembered data, that data belongs in the implementing class.
  • Anything that looks like a field in an interface is a constant. Writing int totalCost = 0; inside an interface silently makes it public static final, and assigning to it is a compile error (§8.15). Interface constants are legal and occasionally useful, but don’t write them by default.

8.5 — Implementing Many Interfaces

Here is the killer feature, and it is one sentence:

A class may extend only one class, but it may implement as many interfaces as it likes.

From code/Wizard.java, with the three contract methods elided so the structure stays visible — the excerpt as printed would not compile, because a class that says implements Healable, Burnable owes all three:

public class Wizard extends Combatant implements Healable, Burnable {
    public Wizard(String name, int hp) { super(name, hp); }

    @Override
    public void takeTurn(Combatant opponent) {
        System.out.println(name + " casts fireball at " + opponent.getName() + " for 12.");
        opponent.receiveDamage(12);

        // Optional capability: only burn what advertises that it can burn.
        if (opponent instanceof Burnable) {
            ((Burnable) opponent).burn(3);
        }
    }

    // heal(), canBeHealed(), and burn() follow — see code/Wizard.java
}

A Wizard inherits Combatant’s structure and honors two independent contracts. Outside code can hold it as a Combatant, as a Healable, or as a Burnable, depending on what that code needs.

This is what C++ attempted with multiple inheritance, and it got ugly fast: two base classes with a common ancestor produce the “diamond problem” — two copies of the same inherited data and an ambiguous answer to “which version of this method do I call?” Java sidesteps it by splitting the two jobs. Classes carry data and implementation, and you get exactly one parent, so the data chain is never ambiguous. Interfaces carry contract only, and you may have any number, because contracts cannot conflict about state they do not own.

The instanceof check inside takeTurn is the other half of the technique, and it is not optional politeness. The compiler will not let you call an interface method straight through a Combatant reference at all, because the base class never promised it. Here is what happens if you try to call heal(10) on one:

B6.java:7: error: cannot find symbol
            c.heal(10);
             ^
  symbol:   method heal(int)
  location: variable c of type Combatant
1 error

So you ask at runtime, and cast only if the answer is yes. This is the legitimate use of instanceof. Chapter 7 warned you off using it to fake polymorphism — a chain of if (x instanceof Trial) … else if (x instanceof Companion) … is a virtual method you refused to write. Probing for an optional capability the base type never declared is a different question, and this is its tool.

Coach’s Note — “Composition over inheritance” gets a third twist in Java: use interfaces to name capabilities. A Healable thing can be healed. A Comparable thing has an ordering. Capabilities cut sideways across a hierarchy — a hero and a garden plant are both healable and share no ancestor worth naming. Inheritance answers “what is it?” Interfaces answer “what can it do?” Ask both about every type you design and the design usually writes itself.


8.6 — Which One Do I Use?

The decision framework, compressed to something you can run in your head during the exam.

Use an abstract class when:

  • There is shared implementation — fields, helper methods, a constructor, a template method.
  • The “is-a” relationship is tight and single: Hero is a Combatant.
  • You do not expect subclasses to need a different parent.
  • You want to force an order of operations (the §8.3 template method).

Use an interface when:

  • You are describing a capability rather than an identity.
  • Classes from unrelated branches need it.
  • There is no shared state to carry.
  • You want a type that future classes can adopt without rewriting their family tree.

Use both — which is what real Java code does. An abstract base for the main type, plus interfaces for the cross-cutting marks. That is exactly the shape of Final Part B: an abstract Encounter for the family, plus small interfaces for capabilities some encounters have and others don’t.

Two tiebreakers for the exam: if you want to put a field in an interface, you wanted an abstract class. If you want a class to have two parents, you wanted an interface.


8.7 — Default Methods (Modern Java)

Java 8 added default methods — interface methods that do have a body:

code/Restorable.java:

public interface Restorable {
    void restore(int amount);
    String getLabel();
    int getCondition();
    int getMaxCondition();

    default boolean isFullyRestored() {
        return getCondition() >= getMaxCondition();
    }
}

Every implementing class gets isFullyRestored() for free. Notice why the default is safe to write: it is expressed entirely in terms of other methods the interface already requires. It touches no state, because it has none to touch.

Any class may override it when its semantics are unusual. In code/RestorableDemo.java, a Manuscript has permanent loss and can never sensibly reach 100, so it overrides:

@Override
public boolean isFullyRestored() {
    return condition >= 90;
}

while Chapel writes nothing and inherits the default. Actual output:

start:
  Fragment 7: 65/100  fullyRestored=false
  Village Chapel: 55/100  fullyRestored=false
after one pass of 30:
  Fragment 7: 95/100  fullyRestored=true
  Village Chapel: 85/100  fullyRestored=false
after a second pass of 20:
  Fragment 7: 100/100  fullyRestored=true
  Village Chapel: 100/100  fullyRestored=true

Both objects sit in the same ArrayList<Restorable> and are asked the same question through the same interface. One answers with the default, one with its override, and the loop never learns which.

Use defaults sparingly. They exist mainly so a library can add a method to an interface without breaking every class that already implemented it. They are not a substitute for an abstract base class — no fields, no constructor, no state.


8.8 — Built-In Interfaces You Will Actually Meet

The Java standard library is built out of interfaces. Three worth knowing by name.

Comparable<T> — “this type has a natural ordering”

code/Witness.java:

public class Witness implements Comparable<Witness> {
    private String name;
    private int century;

    // constructor and getters omitted here — see code/Witness.java

    @Override
    public int compareTo(Witness other) {
        return Integer.compare(this.century, other.century);
    }
}

compareTo returns negative if this sorts before other, zero if they tie, positive if after. Integer.compare(a, b) does that arithmetic correctly — safer than a - b, which overflows on extreme values.

Once a class is Comparable, Collections.sort(list) knows what to do with a list of them. When you want a different order for one call, hand in a Comparator instead. From code/WitnessSort.java:

Collections.sort(list);                       // uses compareTo — by century

list.sort(new Comparator<Witness>() {         // an ordering supplied from outside
    @Override
    public int compare(Witness a, Witness b) {
        return a.getName().compareTo(b.getName());
    }
});

Actual output:

as entered:
  Alpha (century 4)
  Beta (century 2)
  Gamma (century 5)
  Delta (century 1)
  Epsilon (century 3)
sorted by compareTo (century):
  Delta (century 1)
  Beta (century 2)
  Epsilon (century 3)
  Alpha (century 4)
  Gamma (century 5)
sorted by a Comparator (name):
  Alpha (century 4)
  Beta (century 2)
  Delta (century 1)
  Epsilon (century 3)
  Gamma (century 5)

One type, two orderings, zero changes to Witness. Comparable says “here is my one natural order”; Comparator says “here is an order for this occasion.”

Iterable<T> and Runnable

Iterable<T> means “you can walk me with an enhanced-for.” ArrayList implements it, which is precisely why for (Combatant c : party) compiles — you have been using an interface since Chapter 7 without being told. Runnable means “you can run me on another thread”; it is named here only so it isn’t a mystery later. Threading belongs to Coding 3, whose Chapter 8 takes up concurrency properly. It is not on this final.


8.9 — Worked Example: Combat With Capabilities

Six files in code/: Combatant.java (abstract base), Healable.java and Burnable.java (interfaces), Hero.java, Wizard.java, and BattleDemo.java (the driver). The driver’s most instructive method knows nothing about heroes or wizards at all:

public static void capabilityReport(ArrayList<Combatant> party) {
    for (Combatant c : party) {
        String healable = "no";
        if (c instanceof Healable) {
            healable = "yes";
        }
        String burnable = "no";
        if (c instanceof Burnable) {
            burnable = "yes";
        }
        System.out.println(c.getName() + ": HP=" + c.getHp()
                           + ", alive=" + c.isAlive()
                           + ", Healable=" + healable
                           + ", Burnable=" + burnable);
    }
}

Compile the folder with javac *.java, then run java BattleDemo. Actual output:

--- capabilities ---
Maya: HP=100, alive=true, Healable=yes, Burnable=no
Marcus: HP=80, alive=true, Healable=yes, Burnable=yes
Selene: HP=90, alive=true, Healable=yes, Burnable=yes
--- everyone takes 30 ---
Maya: HP=70, alive=true, Healable=yes, Burnable=no
Marcus: HP=50, alive=true, Healable=yes, Burnable=yes
Selene: HP=60, alive=true, Healable=yes, Burnable=yes
--- heal whoever is Healable and needs it ---
Maya heals 20. HP: 90
Marcus heals 20. HP: 70
Selene heals 20. HP: 80
--- turns ---
Maya strikes Marcus for 15.
Marcus casts fireball at Selene for 12.
Selene is burned for 3. HP: 65
Selene casts fireball at Maya for 12.
--- final ---
Maya: HP=78, alive=true, Healable=yes, Burnable=no
Marcus: HP=55, alive=true, Healable=yes, Burnable=yes
Selene: HP=65, alive=true, Healable=yes, Burnable=yes

Three things happened in that transcript, and each is a concept:

  1. The heal loop healed everybody it should, and never mentioned Hero or Wizard. It asked “are you Healable, and do you need it?” and acted on the answer.
  2. Marcus’s fireball on Selene printed a burn line. Selene’s fireball on Maya did not. Identical method, identical call site, different behavior — because Selene implements Burnable and Maya does not. Interface-driven dispatch, visible in the output.
  3. Nothing in BattleDemo had to change to support a third combatant type. Write a class, implement the contracts you want, add it to the list.

That last point is the payoff for eight weeks of type discipline: new behavior added by writing a new class, not by editing an old loop.


8.10 — The Bridge: Why Abstraction and the Final Belong in One Week

These two halves are not stapled together for calendar convenience. They are the same skill from two directions.

An abstract class is a summary the compiler enforces. You look at a family of concrete types — Trials, Companions, Temptations — ask what is genuinely common to all of them, and write that down as a base. Everything specific goes downward into the subclasses. The abstract type is what remains when you remove the particulars.

A cumulative review is that same operation performed on eight weeks of your own work. cout and System.out.println are two implementations of one contract: put a line where the human can see it. struct Manuscript and class Manuscript are two implementations of keep related data together. A C++ virtual method and a Java override are two implementations of the object decides which version runs. The ideas are the abstract base class; the languages are the subclasses.

You are learning to abstract, and reviewing by abstracting — and the exam takes both at once: Part A asks you to read code from every week and say what it prints, Part B asks you to build a system whose whole architecture is an abstract class and a couple of interfaces. Everything from §8.11 forward is written to be re-read in twenty minutes; skim it now, work the review problem, and come back to it before you open the exam.


8.11 — Eight Weeks, Compressed

One paragraph per chapter. If any of these do not ring a bell, stop and re-read that chapter before you sit down to the final.

Chapter 1 — The Sport of Programming and the Memory It Runs On. (Coding 1 chs. 1–2.) Programming is a sport; the skill is articulating a solution precisely enough that a literal machine can follow it. #include <iostream>, using namespace std;, int main(), cout, cin, endl. The five primitive types: int, double, bool, char, string. Declare, initialize, assign. Arithmetic and %. The integer-division trap7 / 2 is 3; use static_cast<double>(a) / b when you need a fraction from two ints.

Chapter 2 — Asking Questions and Doing Them Again. (Coding 1 chs. 3–4.) if / else if / else; == compares and = assigns; &&, ||, ! and short-circuit evaluation; switch for integer dispatch, with break on every case. Never compare doubles with ==. Then for, while, do…while; counters, accumulators, sentinel loops, nested loops. Off-by-one lives here: < versus <=.

Chapter 3 — Functions and Collections. (Coding 1 chs. 5–6.) Return type, name, parameters, body, return. Pass-by-value is the default; & makes it pass-by-reference; const string& for large read-only inputs. Local versus global scope; const globals only. Composition — small trustworthy functions combined into larger ones. Guard clauses. Then fixed-size arrays, 0-based indexing, last valid index size - 1, the array-plus-count pattern, linear search returning -1 for not-found, selection sort. Strings: .length(), .substr(), .find() checked against string::npos (never -1), and cin.ignore() between cin >> n; and a getline.

Chapter 4 — Grouping Memory: Structs — and the Midterm. (Coding 1 chs. 7–8.) A struct bundles related fields under one name; brace initialization; the required semicolon after the closing brace. Pass structs by value, by reference, or by const&. An array of structs replaces parallel arrays and is the last stop before classes. Plus the midterm — the first test you took with the AI off.

Chapter 5 — From Struct to Class: Objects and Encapsulation. (Coding 1 chs. 9–10.) A class is a struct with rules: private data, public methods, the class responsible for its own invariants. this->field when a parameter shadows a field. Constructors run automatically; initializer lists (: field(value)); default and parameterized constructors. Destructors (~Class()) fire at scope exit. Composition — one class holding instances of another. const methods for read-only accessors.

Chapter 6 — Pointers, Dynamic Memory, and Inheritance. (Coding 1 chs. 11–12.) & address-of, * dereference, -> through a pointer, nullptr. new allocates on the heap and delete frees; pair every one with the other. Linked lists: nodes joined by next pointers, and a destructor that walks the chain deleting as it goes. Then class Derived : public Base, protected for shared-with-subclasses data, virtual for dynamic dispatch, override to make the compiler confirm it, virtual destructors whenever polymorphism is in play, pure virtual (= 0) for abstract classes, and slicing — the hazard of passing a derived object by value into a base-typed parameter.

Chapter 7 — Shifting Gears: Java and Polymorphism. (Coding 1 chs. 13–14.) Classes are mandatory; one public class per file, filename matching. System.out.println, Scanner for input. int, double, boolean, char, String — and == compares references while .equals() compares text. Arrays (int[]) and ArrayList<Integer> (no ArrayList<int>). References, not pointers; garbage collection, not delete; no destructors. extends, super(args) first in the constructor. Every method is virtual by default, so there is nothing to mark and no slicing to fear. @Override for safety, the enhanced-for loop, static for class-level members, instanceof and casts used sparingly.

Chapter 8 — Abstraction, Interfaces, and the Final. (Coding 1 chs. 15–16.) abstract class and abstract methods; abstract types cannot be instantiated but can be declared; a base may be partly implemented and call its own abstract methods. interface is pure contract — no state, no constructors. One extends, unlimited implements. instanceof for optional capabilities. default methods. Comparable<T> and Comparator<T>. And the final.

That is the whole course. Two languages, two paradigms, one skill.


8.12 — The Patterns That Repeat

Exams do not test concepts one at a time; they test combinations. Here are the six shapes that show up over and over. Each one is a sketch — a shape to recognize and reproduce, not a file to paste and compile. They name types (Pilgrim, Trial, Strengthening) that you will define yourself in Part B, and a couple of the bodies are left as comments on purpose.

Pattern 1 — Abstract base plus concrete subclasses

public abstract class Encounter {
    protected String name;
    protected int intensity;

    public Encounter(String name, int intensity) { /* assign both */ }

    public abstract void engage(Pilgrim p);
    public String getName() { return name; }
}

Every subclass fills in engage. This is the spine of Part B.

Pattern 2 — Polymorphic collection, one loop

ArrayList<Encounter> route = new ArrayList<>();
route.add(new Trial("Doubt", 5));
route.add(new Companion("Faithful", 3));
route.add(new Temptation("Vanity Fair", 8));

for (Encounter e : route) {
    e.engage(pilgrim);
}

One loop, three behaviors, chosen at runtime. No instanceof belongs in this loop — the whole point is that the object already knows what it is.

Pattern 3 — Interface capability dispatch

for (Encounter e : route) {
    e.engage(pilgrim);

    if (e instanceof Strengthening) {
        ((Strengthening) e).strengthen(pilgrim);
    }
    if (e instanceof Testing) {
        totalCost += ((Testing) e).trialCost();
    }
}

Optional behaviors. The encounters that signed the contract participate; the rest are silently skipped.

Pattern 4 — Loop until a state condition

int step = 0;
while (pilgrim.isAlive() && step < route.size()) {
    route.get(step).engage(pilgrim);
    step++;
}
if (pilgrim.isAlive()) {
    System.out.println("Arrived.");
} else {
    System.out.println("Did not arrive.");
}

Two exit conditions, two endings. Classic exam shape, and pure Chapter 2 underneath.

Pattern 5 — A container class that orchestrates

A Party class holding a private ArrayList<Pilgrim> members, with a runJourney(ArrayList<Encounter> route) method whose outer loop walks the route and whose inner loop calls e.engage(p) on every member that has not given up. A class that owns a collection and drives a sequence of operations across all of it — two nested enhanced-fors and a guard. This is Part B’s Hard tier.

Pattern 6 — The Phase 1 spine inside every Phase 2 program

The thing students forget: objects did not replace the basics; they organize them. Inside every method you write on the final there is still a Chapter 2 loop, a Chapter 2 conditional, and a Chapter 3 linear search — guard clause at the top, running “best so far,” compare, replace. The heaviest() method in code/ReviewWorkout.java is exactly that, wrapped in Chapter 7 syntax. Part A will ask you to trace a loop like it; Part B will require you to write one.


8.13 — A Worked Review Problem

Here is a complete cumulative problem, built the way you should build the final: smallest piece first, compiling at every step. The whole program is in code/ReviewWorkout.java — one file, because only ReviewWorkout is declared public, and Java permits non-public helper classes to share a file.

The problem. A Reader has a name and an understanding value from 0 to 100. A study plan is an ordered list of Resources. Each resource does something different to the reader. Some resources can be cited; some cannot. Run the plan, print a running total, and stop early if the reader gives up.

Step 1 — the thing that holds state. Encapsulation and clamping, straight out of Chapter 5:

class Reader {
    private String name;
    private int understanding;      // 0..100, and the class enforces that

    public Reader(String name, int startingUnderstanding) {
        this.name = name;
        this.understanding = Math.max(0, Math.min(100, startingUnderstanding));
    }

    public void study(int amount)    { understanding = Math.min(100, understanding + amount); }
    public void unsettle(int amount) { understanding = Math.max(0, understanding - amount); }

    public String getName() { return name; }
    public int getUnderstanding() { return understanding; }
    public boolean hasGivenUp() { return understanding <= 0; }
}

Compile it with a three-line main before you write anything else. Always.

Step 2 — the abstract base and one subclass. Do not write three subclasses yet. Write one, run it, prove the override fires:

abstract class Resource {
    protected String title;
    protected int weight;

    public Resource(String title, int weight) {
        this.title = title;
        this.weight = weight;
    }

    public abstract void engage(Reader r);
    public String getTitle() { return title; }
    public int getWeight() { return weight; }
}

class HardQuestion extends Resource {
    public HardQuestion(String title, int weight) { super(title, weight); }

    @Override
    public void engage(Reader r) {
        System.out.println("[Hard question: " + title + "]  -" + weight);
        r.unsettle(weight);
    }
}

Step 3 — the capability, then the subclasses that have it. Reading and Conversation are both citable; HardQuestion is not:

interface Citable {
    String citation();
}

class Reading extends Resource implements Citable {
    public Reading(String title, int weight) { super(title, weight); }

    @Override
    public void engage(Reader r) {
        int gain = weight * 2;
        System.out.println("[Reading: " + title + "]  +" + gain);
        r.study(gain);
    }

    @Override
    public String citation() { return "-> add to bibliography: " + title; }
}

Conversation is the same shape with a smaller gain — see code/ReviewWorkout.java.

Step 4 — the driver. Patterns 2, 3, and 4 in about twenty lines:

public static void runSession(Reader reader, ArrayList<Resource> plan) {
    for (Resource r : plan) {
        if (reader.hasGivenUp()) {
            System.out.println(reader.getName() + " gave up before " + r.getTitle() + ".");
            return;
        }

        r.engage(reader);                     // polymorphism does the dispatch

        if (r instanceof Citable) {           // optional capability
            System.out.println("  " + ((Citable) r).citation());
        }

        System.out.println("  " + reader.getName() + " understanding: "
                           + reader.getUnderstanding());
    }

    if (reader.hasGivenUp()) {
        System.out.println(reader.getName() + " did not finish the plan.");
    } else {
        System.out.println(reader.getName() + " finished the plan at "
                           + reader.getUnderstanding() + ".");
    }
}

Actual output of java ReviewWorkout:

=== Jonah ===
[Reading: The Moral Argument]  +16
  -> add to bibliography: The Moral Argument
  Jonah understanding: 66
[Hard question: But what about suffering?]  -25
  Jonah understanding: 41
[Conversation: Coffee with a skeptic]  +12
  -> add to bibliography: Coffee with a skeptic
  Jonah understanding: 53
[Hard question: Why this faith and not another?]  -30
  Jonah understanding: 23
[Reading: The Resurrection Accounts]  +20
  -> add to bibliography: The Resurrection Accounts
  Jonah understanding: 43
[Conversation: Late call with a friend]  +15
  -> add to bibliography: Late call with a friend
  Jonah understanding: 58
Jonah finished the plan at 58.
heaviest item on the plan: Why this faith and not another? (30)
=== Mara ===
[Hard question: The problem of evil]  -25
  Mara understanding: 0
Mara gave up before A Reply to the Problem of Evil.

Trace two lines yourself, because Part A will ask you to do exactly this:

  • Jonah starts at 50. Reading("The Moral Argument", 8) adds weight * 2 = 16, so he is at 66 — and there is no study() call anywhere in runSession. The object decided.
  • Mara starts at 20 and takes a 25-point hard question. unsettle clamps at 0, not −5, because Reader enforces its own range. The next iteration sees hasGivenUp() and stops.

One small program, and it touched encapsulation, clamping invariants, inheritance, an abstract method, an interface, polymorphic dispatch, an optional-capability check, an ArrayList, an enhanced-for, a guard clause, and a linear find-max — Weeks 2, 3, 5, 6, 7, and 8 in about 150 lines. That is what “cumulative” means.


8.14 — The Study Plan for the Days You Have

You do not have a week of cushion. Here is the honest allocation.

Before Part A (Sessions 1–3):

  1. Do every rep in the exercises, typed. Reps are the only thing that makes code-reading fast, and Part A is timed reading.
  2. Re-read §8.11 twice and §8.12 once. Part A draws from all eight weeks, both languages, and its favorite question is “what does this print?”
  3. Trace by hand. Take any example in this book, cover the output, predict it line by line, then run it. Every mismatch is a gap you found for free.

Before Part B (Session 3, after Part A):

  1. Run one timed practice. Open code/PracticeFinal.java, read only the prompt block, close the file, and solve it from blank in 90 minutes with the AI off. Then compare against the reference solution underneath. Different subject, same shape — you rehearse the shape, not the answer.
  2. Re-read your own Project 6. Your Java migration and polymorphic fleet is the closest thing you own to Part B.

During Part B (Session 4):

  1. Smallest piece first. The state-holding class with a three-line main. Then the abstract base and one subclass. Then the second subclass. Then the loop. Then interfaces. Compile after every one of those steps.
  2. If a feature isn’t working with thirty minutes left, delete it and ship the tier below. A clean Normal beats a broken Medium, and it isn’t close.

What not to do: don’t learn a new Java feature during Part B, don’t design all seven classes on paper before writing any, and don’t open Part B “just to look” in the last hour before it’s due.

Coach’s Note — There is no reference solution to the Pilgrim’s Journey Engine anywhere in this book, and that is deliberate. Part B is a take-home; handing you the answer would make it a typing exercise. PracticeFinal.java is the rehearsal, and it is a generous one — a full prompt with a complete worked solution. Use it in the order the file’s header gives you. Reading the solution first feels like studying and is not.


8.15 — Common Bugs (Week 8 Edition)

Every compiler and runtime message below is real javac or JVM output, captured by compiling the broken code on purpose. The filenames are the scratch files that produced them; yours will show your own. Wording drifts a little between Java versions — the line number and the caret are always the truth.


Bug: You tried to new an abstract type.

B1.java:3: error: Shape is abstract; cannot be instantiated
        Shape s = new Shape();
                  ^

What it means: an abstract class has at least one method with no body; Java will not build an object it cannot fully operate. Fix: instantiate a concrete subclass — Shape s = new Circle(3.0);. If there is no concrete subclass yet, that’s the real problem.


Bug: Your subclass left a hole.

Triangle.java:1: error: Triangle is not abstract and does not override abstract method area() in Shape
public class Triangle extends Shape {
       ^

What it means: Triangle extends Shape but never implemented area(). Fix: implement it, or mark Triangle itself abstract and let its subclasses finish the job. The message names the exact method you owe; if there are two, fix one and recompile to see the next.

The identical message comes from interfaceserror: Plant is not abstract and does not override abstract method canBeHealed() in Healable means you wrote implements Healable and delivered only part of the contract. Add the missing method with the exact signature; a typo in the name counts as missing.


Bug: You gave an abstract method a body.

B4.java:2: error: abstract methods cannot have a body
    public abstract void engage() {
                         ^

Fix: end the declaration with a semicolon — public abstract void engage(); — or drop the abstract keyword and keep the body.


Bug: @Override on something that overrides nothing.

Knight.java:5: error: healUp(int) in Knight does not override or implement a method from a supertype
    @Override
    ^

What it means: the name or the parameter list matches nothing in the parent class or in any implemented interface — healUp versus heal, heal(double) versus heal(int). Fix: correct the signature. Then keep writing @Override on everything — this error is a gift. Without the annotation, Java would have quietly accepted healUp as a brand-new method and your override would silently never run.


Bug: Calling an interface method through a base-class reference.

B6.java:7: error: cannot find symbol
            c.heal(10);
             ^
  symbol:   method heal(int)
  location: variable c of type Combatant

What it means: the object may well be a Hero that can heal, but the variable is typed Combatant, and Combatant never promised heal. The compiler judges by the declared type. Fix: check and cast — if (c instanceof Healable) { ((Healable) c).heal(10); } — or type the collection as ArrayList<Healable> if healing is all you ever do with it. Note that the cast is not optional: writing Healable h = c; gives you error: incompatible types: Combatant cannot be converted to Healable, because Java widens automatically but never narrows for you.


Bug: The cast compiled and then blew up at runtime.

Exception in thread "main" java.lang.ClassCastException: class Hero cannot be cast to class Wizard (Hero and Wizard are in unnamed module of loader 'app')
	at B16.main(B16.java:7)

What it means: you promised the compiler this object was a Wizard. At runtime it was a Hero. This is what an unguarded cast buys you. Fix: never cast without instanceof immediately above it. If you are casting inside a loop over a mixed collection, you almost certainly wanted polymorphism instead.


Bug: You forgot to call super(...).

Squire.java:2: error: constructor Combatant in class Combatant cannot be applied to given types;
    public Squire(String name, int hp) {
                                       ^
  required: String,int
  found:    no arguments
  reason: actual and formal argument lists differ in length

What it means: a constructor that does not start with an explicit super(...) gets an implicit super() — and Combatant has no no-argument constructor. Fix: make super(name, hp); the first statement in the subclass constructor. Newer Java versions will tolerate a statement or two ahead of it, but only if those statements do not touch the object being built — a nuance you will never need this week. Put super(...) first and move on.


Bug: You implemented an interface method without public.

Sapling.java:4: error: heal(int) in Sapling cannot implement heal(int) in Healable
    void heal(int amount) { hp += amount; }
         ^
  attempting to assign weaker access privileges; was public

What it means: interface methods are implicitly public. Your implementation dropped the modifier, which would narrow access — not allowed. Fix: write public on every method that implements an interface. A related clash: if two interfaces both declare weight() and one returns int while the other returns String, no class can implement both (error: weight() in Relic cannot implement weight() in Sized / return type int is not compatible with String) — rename one. If the signatures match, though, there is no problem at all; one method satisfies both contracts.


Bug: You assigned to something you thought was an interface field.

Ledger.java:3: error: cannot assign a value to static final variable totalCost
    public void spend(int amount) { totalCost = totalCost + amount; }
                                    ^

What it means: interfaces hold no instance state. int totalCost = 0; written inside an interface is silently public static final — one shared constant for the whole program, not a field on each object (§8.4). You can read it; you can never assign to it. Fix: move the field into the implementing class, where per-object state belongs. If you actually wanted a shared constant, leave it in the interface and stop writing to it.


Bug: NullPointerException.

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Combatant.getName()" because "<local1>[<local2>]" is null
	at NpeDemo.main(NpeDemo.java:6)

What it means: you called a method on a reference that pointed at nothing. Combatant[] party = new Combatant[3]; gives you three null slots, not three combatants, and filling only slot 0 leaves two landmines. <local1>[<local2>] is Java saying “an element of an array local variable,” since the compiler wasn’t asked to keep variable names. Fix: fill every slot, loop only to the count you actually filled, or use an ArrayList, where size() and reality cannot disagree.


Bug: ArrayIndexOutOfBoundsException.

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2
	at B18.main(B18.java:5)

Fix: classic off-by-one — write <, not <=. Valid indices run 0 to length - 1. (Also still true from Chapter 7: ArrayList<int> will not compile; generics hold reference types only.)


Bug: == on Strings. No error at all — just a wrong answer.

Not a compiler message this time — a console session. Read a line with String typed = in.nextLine();, then print both comparisons. The user types Trial and presses Enter:

type a word: Trial
typed == "Trial"        -> false
typed.equals("Trial")   -> true

What it means: the user typed exactly Trial, and == still said false, because == compares references and the Scanner built a new String object. Literals written in your source get pooled and often compare true with ==, which makes this bug hide during testing and appear on the exam. Fix: .equals() for text, every time, without exception.


8.16 — Reps

Three to prove you were awake. The full set is in the exercises — the last conditioning you get before the exam, so do all of it.

Rep 1. Write an abstract Shape with abstract area() and kind() and a concrete describe(). Add Circle and Rectangle, put both in a Shape[], and loop.

Rep 2. Write a Healable interface and implement it on two classes that share no base class at all — say a Hero and a Sapling. Put both in one ArrayList<Healable> and heal them in a single loop.

Rep 3. Write one class that extends an abstract base and implements two interfaces. Then loop over the base type using instanceof to fire each optional capability, and confirm from the output that they fire only for the classes that declared them.


8.17 — Checkpoint: Can You Do This Yet?

Blank file, no notes, no book, no AI. Give yourself thirty minutes.

  1. Write an abstract class with one field, one constructor, one abstract method, and one concrete method — from memory, syntax exact.
  2. Write, from memory, the concrete subclass that makes it compile, including super(...) and @Override.
  3. Say out loud, in one sentence, why new Shape() is a compile error and Shape s = new Circle(3.0); is not.
  4. Write an interface with two methods and a class that implements it.
  5. Write a class that extends one class and implements two interfaces, in the correct keyword order.
  6. Write the four-line instanceof-check-then-cast idiom for an optional capability.
  7. State the two rules of thumb from §8.6: when abstract class, when interface.
  8. Given a for (Encounter e : route) { e.engage(p); } loop over a mixed list, explain which engage runs for each element and when that decision is made.

Pass bar: 6 of 8, with items 1, 2, and 4 among them.

If you scored below that, do not open Final Part B yet. Go back to §8.1–§8.5, retype code/Shape.java through code/BattleDemo.java without looking at them, and redo Reps 1–3. That costs you an hour. Walking into a four-hour practical unable to write an abstract class from memory costs you the practical.


8.18 — When You’re Stuck (and Nobody’s in the Room)

It is the last week, you are alone, and something does not compile. Work the ladder in order. Do not skip to the bottom — steps 1 through 3 solve most Week 8 problems in under ten minutes.

1. Read the error, all of it, from the top. Java errors this week are unusually literal — Triangle is not abstract and does not override abstract method area() in Shape is a to-do list, not a complaint. Fix the first error and recompile; one missing method can cascade into six messages, five of which vanish on their own. Then check §8.15, which has this week’s whole realistic error set.

2. Ask the three Week 8 questions. Nearly every failure this week is one of these:

  • Did I implement every abstract method? Count the abstract declarations in the base; count the @Overrides in the subclass.
  • Is my method public? Interface implementations must be.
  • Am I calling through the right type? If the variable is declared Combatant, you can only call Combatant methods, whatever the object really is.

3. Cut it down to two classes. In a scratch OnlineGDB project: one tiny abstract class with one abstract method, one subclass, one main that prints one line. If that compiles and yours does not, diff them line by line. If that does not compile either, the misunderstanding is in the syntax and §8.1 is a five-minute reread. Faster than staring, every time.

4. Read the contract out loud. Literally speak it: “A Healable thing must provide heal(int) and canBeHealed(). Does my class? Public? Spelled right? Same parameter type?” Interface bugs are usually a spelling or a modifier, and speaking the contract catches both faster than reading it does.

5. Add a print, not a theory. If it compiles but misbehaves, print the state at the top of every engage/takeTurn: the name, the field, the class you think you are in. Three println lines find polymorphism bugs faster than an hour of reasoning. The classic discovery: your @Override never fired because a signature drifted.

6. Search this book, not the internet. Appendix C is the C++/Java reference table; Appendix D is the glossary. Chapter 7 covers @Override, instanceof, and casts; Chapter 6 covers the C++ side if a comparison helps.

7. Post on the discussion board. Title it with the error, not the feeling: does not override abstract method engage(Pilgrim) — 3 subclasses, only one fails.” In the body: the smallest code that reproduces it, the full error text, and one sentence of what you tried. Posts shaped like that get answered fast — and you often solve it while writing.

8. Email the instructor. Use this, filled in:

Subject: Ch8 blocker — <the exact first line of the error>

Where I am: Week 8, <section §8.x / rep N / Part B, tier X>.
What I ran:  javac *.java   (or: the exact command)
Error, in full:
  <paste every line, including the caret line>
Smallest code that reproduces it:
  <10-20 lines, not the whole project>
What I already tried:
  1. <thing>
  2. <thing>
My guess at the cause: <one sentence>

A boundary that matters this week. Steps 1–8 apply to the chapter, the reps, and Final Part A preparation. Once you open Final Part B, the help channels narrow to logistics only — submission format, a broken link, a deadline problem. Nobody, including the discussion board, will debug your exam code, and asking is itself a problem. That is what makes a take-home worth taking. Which is exactly why steps 1–5 are worth practicing this week, on the reps, where they are free: during Part B that ladder is the only help you get, and it is enough.


8.19 — This Week’s Exam

There is no project this week. The graded item is the FINAL, in two parts. Both are posted in Canvas; both live in the Final category, which is 25% of your course grade (Projects are 50%, the Midterm 25%). Part A is 50 points, Part B is 100150 together. The auto-graded half is deliberately the smaller one: this exam is unproctored, and the program you write cold is the real evidence.

Part A — Code Reading & Tracing (50 points, auto-graded)

A Canvas quiz drawn from a question pool: 25 multiple choice, 10 true/false, and 5 matching sets, covering Weeks 1 through 8 — both languages, both paradigms. Not trivia. It is a programming exam made of reading: what does this print, which line causes the error, what is the value of count after this loop. Expect C++ from Weeks 1–6 and Java from Weeks 7–8, side by side, on purpose.

It is auto-graded, so you get your score the moment you submit, and every question carries a written rationale — read those even for the ones you got right. Budget an unhurried block. The book cannot answer “what does this print” for you; the tracing either happens in your head or it doesn’t.

Part B — Pilgrim’s Journey Engine (100 points, take-home practical, Java)

The practical: roughly four focused hours, submitted as an OnlineGDB project link (see Appendix A). The due window is stated on the Canvas assignment — read it before Session 4, not during.

The setting is John Bunyan’s The Pilgrim’s Progress (1678), the most-read Christian allegory in English: a pilgrim travels toward the Celestial City and meets trials, companions, and temptations, each of which does something different to him. You are not writing Bunyan’s plot; you are building the engine — the machinery underneath a journey where every encounter behaves differently and the loop doesn’t care.

The assignment will require you to:

  • Define an abstract base class with at least one abstract method.
  • Define two or more concrete subclasses, each with a real @Override implementation.
  • Hold a Pilgrim whose state is encapsulated and clamped by its own methods.
  • Drive the whole thing from an ArrayList of the base type, where polymorphism does the dispatch — no instanceof chain, no switch on type.
  • At the Medium tier, add interfaces that cut across the hierarchy.
  • At the Hard tier, add a container class that orchestrates the sequence over a whole collection.

If §8.13’s worked review problem felt doable, this is the same shape. Normal tier is passing; Medium and Hard are extra credit, exactly as on every project this term.

On integrity, plainly. Part B is unproctored, and the design accounts for that rather than pretending otherwise. Two things are built into the submission: an authorship block at the top of your main file, and short written answers about your own design decisions — why you made that class abstract, why that capability is an interface rather than a method on the base, what you would change. Only the person who wrote the code can answer those. The policy is simple: AI-generated code you cannot explain is treated as not submitted. No lecture attached — the exam is a measurement, and a measurement of someone else’s work tells you nothing you can use.

The midterm was the first test you took with the AI off; this is the second. If the midterm felt reasonable and you have stayed honest since, Part B will feel like a project you have already built small versions of. If it didn’t, the final says the same thing again, and the number is information rather than a verdict.

A theological footnote, because the theme has earned one. Bunyan wrote from the Puritan tradition; his theology of conversion, perseverance, and assurance differs in places from confessional Lutheranism. The shape of the allegory — pilgrim, trial, companion, temptation — is shared across Christian traditions, and the engine models the shape, not the doctrine. It no more endorses Bunyan’s soteriology than it endorses Dante’s cosmology. If you would rather frame your encounters explicitly Lutheran — the will in bondage, freed only by grace — the engine does not care, and neither does the rubric.


8.20 — Coach’s Final Word for Week 8

You now hold the entire toolkit: variables, control flow, functions, arrays and strings, structs, classes, encapsulation, pointers and dynamic memory, inheritance, polymorphism, abstract classes, and interfaces. In two languages — and with the experience most beginners never get, of watching one idea wear two syntaxes and stay the same idea.

That is a great deal for eight weeks, and you did it without a lecture, without a lab, and without anyone in the room. Do not let the format make you undersell it. Most people who “take an intro programming course” can talk about objects afterward; far fewer can sit down to a blank file and build a small working system. You can. The final is where you prove it to yourself, which is the only audience that matters.

Coach’s Note — what we deliberately skipped, and where it goes. This course did not cover (a) try/catch exception handling, (b) toString() and equals/hashCode overrides, or (c) generics beyond ArrayList<T>. Those are core Java idioms, not optional polish; they were left out to keep eight weeks focused on the OOP toolkit itself. Coding 2 picks all three up — exception handling in its Chapter 3, collections and generics in its Chapter 8, and the equals/hashCode/toString overrides alongside them as they become necessary. If during Part B you hit a NullPointerException and wish you could handle it instead of crashing — that instinct is correct, and it is close to the first thing the next course teaches.

Two last honest things.

Programming is a craft, and crafts decay. The skill you have at the end of this week is real but young. The people who keep it keep using it — a side project, a puzzle, a small tool for somebody they know. The people who lose it lose it inside a year of stopping. Twelve hours a week got you here; two hours a week keeps you here.

The same holds for the apologetics frame, if it has done any work on you. You can finish a semester of reading about hard questions; the questions come back, year after year, in the lives of people you love. Thinking carefully about a hard thing — and typing cleanly while you do it — is a skill you will use for the rest of your life, if you keep it.

Show up rested. Trust your training. Submit something honest.


Up next: Work every rep in the exercises and take the §8.17 Checkpoint. Then do the timed run in code/PracticeFinal.java. Then Final Part A, then Final Part B — Pilgrim’s Journey Engine, both in Canvas.

That’s the course. After the final, you’re done — and you’re a programmer.

Check Your Reps

Week 8 Knowledge Check

Question 1 of 6
What does this print?
abstract class Animal {
    abstract String sound();
    public String toString() { return getClass().getSimpleName() + " says " + sound(); }
}
class Cow extends Animal { String sound() { return "moo"; } }

Animal x = new Cow();
System.out.println(x);
Why: toString() is a concrete method inherited from the abstract base, but the sound() it calls dispatches to Cow's version. An abstract class can mix methods it fully implements with ones it only declares — that combination is the reason to choose it.
Question 2 of 6
Why can you not write `new Shape()` when Shape declares an abstract method?
Why: An abstract class is a promise that subclasses complete. You instantiate a concrete subclass and may then hold it in a variable of the abstract type — which is exactly how polymorphism gets its handle.
Question 3 of 6
When should you choose an interface over an abstract class?
Why: Abstract class for an is-a relationship with shared state and behavior; interface for a can-do capability that unrelated types promise. A class extends one class but may implement many interfaces.
Question 4 of 6
A class implements two interfaces. What must it provide?
Why: The interface states the promise; the class keeps it, in full. Implementations must be public — narrowing the visibility of an interface method is a compile error, and omitting the keyword is a common first attempt.
Question 5 of 6
You store a Manuscript in a variable declared as one of the interfaces it implements. What changes?
Why: The object is untouched — the declared type limits the visible surface. Casting back (after an instanceof check) restores access to the rest.
Question 6 of 6
The final has two parts. What is the honest reason Part B is worth twice Part A?
Why: The auto-graded half checks that you can read code; the practical checks that you can write it. Only one of those is the actual job — so the weighting reflects that, and so does the requirement that you explain your own design decisions in writing.
YOU FINISHED. NICE WORK.