Skip to content

Lab: subscription plans that override

☕ Java 17 · Rewriting What Your Parent Class Does · 3/3

Goal: build three subscription plans where each one overrides only what differs, while a final method keeps one format for all.

Setup: create SubscriptionPlans.java (no package). All classes go in this file. Compile with javac SubscriptionPlans.java, run with java SubscriptionPlans.

Step 1 — The basic plan

class Subscription {
    String name() { return "Basic"; }
    int price() { return 10; }

    final String describe() {
        return name() + ": $" + price();
    }
}

public class SubscriptionPlans {
    public static void main(String[] args) {
        Subscription basic = new Subscription();
        System.out.println(basic.describe());
    }
}

describe() is final: every plan will describe itself in the same format.

Basic: $10

Step 2 — The premium plan

Add between Subscription and SubscriptionPlans:

class PremiumPlan extends Subscription {
    @Override String name() { return "Premium"; }
    @Override int price() { return super.price() + 8; }
}
  • @Override makes the compiler check that each method really overrides one.
  • super.price() reuses the parent's 10. Without super, price() would call itself until a StackOverflowError.
  • PremiumPlan doesn't touch describe(): it can't (it's final) and doesn't need to.

In main, note the variable's type, Subscription:

        Subscription premium = new PremiumPlan();
        System.out.println(premium.describe());
Basic: $10
Premium: $18

describe() lives in Subscription, but when it calls name() and price(), the real object's versions run.

Step 3 — The family plan

class FamilyPlan extends PremiumPlan {
    @Override String name() { return "Family"; }
    @Override int price() { return super.price() + 5; }
}

Here super.price() is PremiumPlan's, the direct parent. In main:

        Subscription family = new FamilyPlan();
        System.out.println(family.describe());
Basic: $10
Premium: $18
Family: $23

FamilyPlan.price() → PremiumPlan.price() → Subscription.price() = 10; then 10 + 8 = 18; then 18 + 5 = 23.

Final code

class Subscription {
    String name() { return "Basic"; }
    int price() { return 10; }
    final String describe() {
        return name() + ": $" + price();
    }
}

class PremiumPlan extends Subscription {
    @Override String name() { return "Premium"; }
    @Override int price() { return super.price() + 8; }
}

class FamilyPlan extends PremiumPlan {
    @Override String name() { return "Family"; }
    @Override int price() { return super.price() + 5; }
}

public class SubscriptionPlans {
    public static void main(String[] args) {
        Subscription basic = new Subscription();
        System.out.println(basic.describe());
        Subscription premium = new PremiumPlan();
        System.out.println(premium.describe());
        Subscription family = new FamilyPlan();
        System.out.println(family.describe());
    }
}

Full code on GitHub

← @Override, hiding and final · Back to the series


🎓 Preparing the OCP Java 17 exam? Exam traps and practice questions for this section → (optional)