CompSci.rocks
FRQcsapa

TaxableItem / Vehicle: 2006 FRQ 2

A step-by-step solution to the 2006 AP CSA FRQ 2 (TaxableItem/Vehicle), covering finishing an abstract parent class's method and building a subclass on top of it with a two-part list price in Java.

A small hierarchy of purchasable items — some taxed, some not — sets up this AP Computer Science A free-response question, where you first finish a shared parent method and then build an entire subclass around it.

What This FRQ Tests

  • AP CSA units: Unit 5 (Writing Classes) and Unit 9 (Inheritance)
  • Core skill: finishing a method that calls an abstract method it doesn't (and can't) know the concrete implementation of
  • Secondary skill: designing a subclass's own fields and constructor, then forwarding shared data up to the parent with super(...)
  • Official category: "Classes" — officially printed as FRQ 2 on the 2006 exam, matching where "Classes" questions still land on today's exam, though that fixed per-slot pattern itself wasn't standardized until years later.

The Setup

  • The given Item interface requires one method: double purchasePrice();
  • The given TaxableItem class (abstract, implements Item) has:
    • private double taxRate
    • public abstract double getListPrice() — every subclass must supply its own version
    • A constructor: TaxableItem(double rate)
    • double purchasePrice() — to be written in part (a)
  • You're asked to write an entire new class, Vehicle, that:
    • Extends TaxableItem
    • Has a dealer cost and a dealer markup, whose sum is the vehicle's list price
    • Has a constructor taking dealer cost, dealer markup, and tax rate
    • Has a public method changeMarkup(double newMarkup) that updates the dealer markup

Part (a): Writing purchasePrice()

The Rule, Broken Down

  1. The purchase price of a taxable item is its list price, plus tax computed on that list price.
  2. Tax is listPrice * taxRate — for a 10% tax rate (0.10) and a $6.50 list price, that's $0.65 of tax, for a $7.15 total.
  3. purchasePrice() lives in TaxableItem, but getListPrice() is abstract here — its real behavior depends entirely on whichever subclass (like Vehicle) eventually implements it.

Step-by-Step Approach

  1. Get the item's list price by calling getListPrice() — even though TaxableItem itself has no idea how that number is computed, Java will call whichever version the actual object (a Vehicle, or any other subclass) provides.
  2. Multiply that list price by taxRate to get the tax amount.
  3. Return the list price plus the tax amount.

The Code

public double purchasePrice()
{
    return getListPrice() + getListPrice() * taxRate;
}

Why Each Piece Matters

  • Calling getListPrice() instead of a fieldTaxableItem has no list-price field of its own; getListPrice() is abstract specifically so each subclass can define list price however makes sense for it (a Vehicle's is dealer cost plus markup, but a different subclass could compute it completely differently).
  • taxRate is used directly, without a getter — it's declared right there as a private field of TaxableItem itself, so purchasePrice() (also defined in TaxableItem) can access it without any extra plumbing.
  • getListPrice() * taxRate, not getListPrice() * (1 + taxRate) combined into one call — either formula is mathematically valid, but calling getListPrice() once and reusing that result (double listPrice = getListPrice(); return listPrice + listPrice * taxRate;) can be a slightly cleaner style if getListPrice() were an expensive computation; here it's simple enough that calling it twice, as written above, causes no real issue.

Tracing the Example

Using the value given directly in the prompt: taxRate = 0.10, getListPrice() returns 6.50.

  • getListPrice() + getListPrice() * taxRate6.50 + 6.50 * 0.106.50 + 0.657.15

This matches the problem's stated result exactly.

Common Mistakes to Avoid

  • Treating getListPrice() as if it were a plain field (listPrice + listPrice * taxRate without ever calling the method) — TaxableItem doesn't store a list price itself; it only knows how to ask for one.
  • Forgetting the tax entirely and just returning getListPrice() — that's the list price, not the purchase price.
  • Multiplying taxRate by itself or a hardcoded number instead of getListPrice() — the tax has to scale with each specific item's list price.

Part (b): Building the Vehicle Class

The Rule, Broken Down

  1. Vehicle extends TaxableItem, so it inherits purchasePrice() for free — the only new work is supplying getListPrice() and the vehicle-specific data behind it.
  2. A vehicle's list price is dealerCost + dealerMarkup — two separate stored numbers, not one combined field.
  3. The constructor takes all three inputs a vehicle needs: dealer cost, dealer markup, and tax rate — the tax rate has to be forwarded to TaxableItem's constructor, since taxRate is private there and can't be set directly.
  4. changeMarkup(double newMarkup) lets the dealer markup be updated after construction.

Step-by-Step Approach

  1. Declare the class as public class Vehicle extends TaxableItem.
  2. Add two new fields: private double dealerCost and private double dealerMarkup.
  3. Write a constructor taking (double cost, double markup, double rate): forward rate to TaxableItem with super(rate), then assign dealerCost and dealerMarkup from the other two parameters.
  4. Implement getListPrice(), returning dealerCost + dealerMarkup — this satisfies the abstract method TaxableItem requires, which is also what lets purchasePrice() (inherited, unchanged) work correctly for a Vehicle.
  5. Add changeMarkup(double newMarkup), which simply reassigns dealerMarkup.

The Code

public class Vehicle extends TaxableItem
{
    private double dealerCost;
    private double dealerMarkup;

    public Vehicle(double cost, double markup, double rate)
    {
        super(rate);
        dealerCost = cost;
        dealerMarkup = markup;
    }

    public double getListPrice()
    {
        return dealerCost + dealerMarkup;
    }

    public void changeMarkup(double newMarkup)
    {
        dealerMarkup = newMarkup;
    }
}

Why Each Piece Matters

  • super(rate)taxRate is private inside TaxableItem, so Vehicle has no way to set it directly; the only way to initialize it is through TaxableItem's own constructor.
  • getListPrice() returning dealerCost + dealerMarkup — this is the one piece TaxableItem couldn't provide on its own (that's exactly why it was declared abstract), and it's what makes the inherited purchasePrice() produce a correct answer for a Vehicle without Vehicle needing to override purchasePrice() itself.
  • No taxRate field inside VehicleVehicle never needs to read or store taxRate itself; it only ever needs purchasePrice(), which is already fully written in TaxableItem and uses taxRate internally there.
  • changeMarkup reassigns dealerMarkup, not dealerCost — only the markup is described as changeable; the dealer cost isn't given a setter at all, since nothing in the problem asks for one.

Tracing the Example

Using the values given directly in the prompt: dealer cost $20,000.00, dealer markup $2,500.00, tax rate 0.10.

Step List price (dealerCost + dealerMarkup) Purchase price (listPrice + listPrice * taxRate)
Initial 20000 + 2500 = 22500.00 22500 + 22500(0.10) = 24750.00
After changeMarkup(1000.00) 20000 + 1000 = 21000.00 21000 + 21000(0.10) = 23100.00

Both rows match the problem's stated results exactly — including the second one, which confirms getListPrice() correctly reflects the updated markup the next time purchasePrice() is called (nothing needs to be recomputed or re-stored ahead of time, since getListPrice() always reads the current dealerMarkup).

Common Mistakes to Avoid

  • Forgetting super(rate), which either fails to compile (if TaxableItem has no no-argument constructor) or leaves taxRate uninitialized.
  • Storing a single combined listPrice field instead of dealerCost and dealerMarkup separately — this breaks changeMarkup, since there'd be no way to recover just the cost portion after the markup changes.
  • Not implementing getListPrice() at all — since it's abstract in TaxableItem, leaving it out means Vehicle won't compile as a concrete (non-abstract) class.
  • Overriding purchasePrice() in Vehicle unnecessarily. It's already correctly inherited from TaxableItem once getListPrice() is implemented — rewriting it here would just duplicate logic that already works.

Key Takeaways

  • An abstract method in a parent class is a placeholder the parent's other, already-written methods can still safely call — the parent doesn't need to know how it'll be implemented, only that it will be, by every concrete subclass.
  • A subclass that needs to set a private field it inherits has exactly one path in: the parent's constructor, via super(...).
  • When a subclass's only real job is supplying the missing piece an abstract parent method needs (like getListPrice()), there's usually no reason to override that parent method itself — implementing the missing piece is enough to make the whole inherited chain work correctly.

Related FRQs