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
Iteminterface requires one method:double purchasePrice(); - The given
TaxableItemclass (abstract,implements Item) has:private double taxRatepublic 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
- Extends
Part (a): Writing purchasePrice()
The Rule, Broken Down
- The purchase price of a taxable item is its list price, plus tax computed on that list price.
- 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. purchasePrice()lives inTaxableItem, butgetListPrice()isabstracthere — its real behavior depends entirely on whichever subclass (likeVehicle) eventually implements it.
Step-by-Step Approach
- Get the item's list price by calling
getListPrice()— even thoughTaxableItemitself has no idea how that number is computed, Java will call whichever version the actual object (aVehicle, or any other subclass) provides. - Multiply that list price by
taxRateto get the tax amount. - 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 field —TaxableItemhas no list-price field of its own;getListPrice()isabstractspecifically so each subclass can define list price however makes sense for it (aVehicle's is dealer cost plus markup, but a different subclass could compute it completely differently). taxRateis used directly, without a getter — it's declared right there as a private field ofTaxableItemitself, sopurchasePrice()(also defined inTaxableItem) can access it without any extra plumbing.getListPrice() * taxRate, notgetListPrice() * (1 + taxRate)combined into one call — either formula is mathematically valid, but callinggetListPrice()once and reusing that result (double listPrice = getListPrice(); return listPrice + listPrice * taxRate;) can be a slightly cleaner style ifgetListPrice()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() * taxRate→6.50 + 6.50 * 0.10→6.50 + 0.65→7.15
This matches the problem's stated result exactly.
Common Mistakes to Avoid
- Treating
getListPrice()as if it were a plain field (listPrice + listPrice * taxRatewithout ever calling the method) —TaxableItemdoesn'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
taxRateby itself or a hardcoded number instead ofgetListPrice()— the tax has to scale with each specific item's list price.
Part (b): Building the Vehicle Class
The Rule, Broken Down
VehicleextendsTaxableItem, so it inheritspurchasePrice()for free — the only new work is supplyinggetListPrice()and the vehicle-specific data behind it.- A vehicle's list price is
dealerCost + dealerMarkup— two separate stored numbers, not one combined field. - 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, sincetaxRateisprivatethere and can't be set directly. changeMarkup(double newMarkup)lets the dealer markup be updated after construction.
Step-by-Step Approach
- Declare the class as
public class Vehicle extends TaxableItem. - Add two new fields:
private double dealerCostandprivate double dealerMarkup. - Write a constructor taking
(double cost, double markup, double rate): forwardratetoTaxableItemwithsuper(rate), then assigndealerCostanddealerMarkupfrom the other two parameters. - Implement
getListPrice(), returningdealerCost + dealerMarkup— this satisfies theabstractmethodTaxableItemrequires, which is also what letspurchasePrice()(inherited, unchanged) work correctly for aVehicle. - Add
changeMarkup(double newMarkup), which simply reassignsdealerMarkup.
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)—taxRateisprivateinsideTaxableItem, soVehiclehas no way to set it directly; the only way to initialize it is throughTaxableItem's own constructor.getListPrice()returningdealerCost + dealerMarkup— this is the one pieceTaxableItemcouldn't provide on its own (that's exactly why it was declaredabstract), and it's what makes the inheritedpurchasePrice()produce a correct answer for aVehiclewithoutVehicleneeding to overridepurchasePrice()itself.- No
taxRatefield insideVehicle—Vehiclenever needs to read or storetaxRateitself; it only ever needspurchasePrice(), which is already fully written inTaxableItemand usestaxRateinternally there. changeMarkupreassignsdealerMarkup, notdealerCost— 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 (ifTaxableItemhas no no-argument constructor) or leavestaxRateuninitialized. - Storing a single combined
listPricefield instead ofdealerCostanddealerMarkupseparately — this breakschangeMarkup, since there'd be no way to recover just the cost portion after the markup changes. - Not implementing
getListPrice()at all — since it'sabstractinTaxableItem, leaving it out meansVehiclewon't compile as a concrete (non-abstract) class. - Overriding
purchasePrice()inVehicleunnecessarily. It's already correctly inherited fromTaxableItemoncegetListPrice()is implemented — rewriting it here would just duplicate logic that already works.
Key Takeaways
- An
abstractmethod 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
privatefield it inherits has exactly one path in: the parent's constructor, viasuper(...). - 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.