Advance / StudentAdvance: 2005 FRQ 2
A step-by-step solution to the 2005 AP CSA FRQ 2 (Ticket/Advance/StudentAdvance), covering inheritance, overriding getPrice() and toString(), and polymorphic method dispatch in Java.
A three-level class hierarchy for theater tickets is the backdrop for this AP Computer Science A free-response question — you write two subclasses that each override just enough behavior to get a different price and a different printed ticket, while reusing everything else from their parent.
What This FRQ Tests
- AP CSA units: Unit 9 (Inheritance) and Unit 5 (Writing Classes)
- Core skill: extending a class, overriding an abstract method, and calling
super.getPrice()/super.toString()to build on inherited behavior instead of duplicating it - Secondary skill: recognizing that a method called from inside an inherited method (like
getPrice()being called fromTicket'stoString()) still dispatches to the most specific override at runtime — this is what letsStudentAdvanceprint its halved price withoutTicket'stoString()ever being rewritten - Official category: "Classes" — this one does match the modern FRQ 2 slot (Classes/Inheritance), and in this case the 2005 exam's own printed "2." happens to agree. Treat that as this particular year's outcome rather than a guaranteed rule for every pre-2019 exam — the fixed FRQ-number-to-category ordering wasn't standardized until the 2019-2020 Course and Exam Description redesign.
The Setup
- The given
Ticketclass (abstract, not to modify) provides:private int serialNumber— assigned automatically in the constructorpublic Ticket()— setsserialNumberusing an already-implemented helperpublic abstract double getPrice()— every subclass must implement thispublic String toString()— already implemented as"Number: " + serialNumber + "\nPrice: " + getPrice()
Walkup(given, described only — you don't write it) extendsTicketand always costs a flat $50.- You're asked to write two complete class declarations:
Advance extends Ticket— constructor takesdaysInAdvance; costs $30 if purchased 10 or more days ahead, $40 otherwiseStudentAdvance extends Advance— constructor also takesdaysInAdvance; always costs half of whatever the equivalentAdvanceticket would cost, and itstoString()adds a"(student ID required)"line
- The catch for
StudentAdvance: it has to keep computing the correct halved price even ifAdvance's pricing rule changes later, without touchingStudentAdvance's own code.
Part (a): Building the Advance Class
The Rule, Broken Down
Advanceneeds one new piece of information: how many days in advance it was purchased.- If that's 10 or more, the price is $30; otherwise, it's $40.
Advancedoesn't need its owntoString()— the inherited one fromTicketalready produces"Number: <serialNumber>\nPrice: <price>", which matches the sample output exactly.
Step-by-Step Approach
- Declare
Advanceaspublic class Advance extends Ticket. - Give it one field to hold the computed price (there's no need to keep
daysInAdvanceitself around once the price is decided). - In the constructor, check
daysInAdvanceagainst the 10-day cutoff and store the resulting price. - Implement the required abstract method,
getPrice(), returning that stored price.
The Code
public class Advance extends Ticket
{
private double price;
public Advance(int daysInAdvance)
{
if (daysInAdvance >= 10)
{
price = 30;
}
else
{
price = 40;
}
}
public double getPrice()
{
return price;
}
}
Why Each Piece Matters
- No explicit
super(...)call is needed. Java automatically inserts a call to the no-argumentTicket()constructor at the start ofAdvance's constructor, which is exactly what assignsserialNumber. priceis computed once, in the constructor — since anAdvanceticket's price never changes after it's purchased, there's no reason to recompute theif/elseevery timegetPrice()is called.daysInAdvance >= 10, not> 10— the rule specifically says "ten or more days," so exactly 10 days must land in the cheaper bracket.- No
toString()override here —Ticket's existingtoString()already callsgetPrice(), which now correctly returnsAdvance's price. Rewriting the formatting would just duplicate logic that already works.
Tracing the Example
Using the question's own sample output for Advance, "Number: 357\nPrice: 40" — a ticket bought fewer than 10 days ahead:
new Advance(5)→5 >= 10isfalse→price = 40.toString()(inherited fromTicket) →"Number: " + serialNumber + "\nPrice: " + getPrice()→"Number: 357\nPrice: 40"
The serial number itself (357) comes from Ticket's own serial-number generator, which isn't something this solution controls or needs to reproduce — only the Price: 40 portion is being verified here, and it matches.
Common Mistakes to Avoid
- Using
> 10instead of>= 10. A ticket bought exactly 10 days ahead must get the $30 price, not the $40 one. - Overriding
toString()unnecessarily. It works without doing anything wrong, but it's extra code that duplicates whatTicketalready provides — and it isn't needed to match the sample output. - Storing
daysInAdvanceand recomputing the price insidegetPrice()every call. Not incorrect, just more work than needed, since the price never changes after construction.
Part (b): Building the StudentAdvance Class
The Rule, Broken Down
StudentAdvanceextendsAdvance, notTicketdirectly — it inheritsAdvance's pricing rule as its starting point.- Its price is always half of what the same
daysInAdvancewould produce as a plainAdvanceticket. - Its
toString()needs everythingAdvance's version already prints, plus one extra line:"(student ID required)". - The halving must be based on calling
Advance's actual pricing logic — not on rewriting the $30/$40 rule a second time — so that a future change toAdvance's prices automatically flows through.
Step-by-Step Approach
- Declare
public class StudentAdvance extends Advance. - Write a constructor that takes
daysInAdvanceand forwards it withsuper(daysInAdvance)— this is required here, sinceAdvancehas no no-argument constructor for Java to call automatically. - Override
getPrice()to returnsuper.getPrice() / 2— calling the parent's version instead of re-deriving the $30/$40 rule. - Override
toString()to returnsuper.toString() + "\n(student ID required)".
The Code
public class StudentAdvance extends Advance
{
public StudentAdvance(int daysInAdvance)
{
super(daysInAdvance);
}
public double getPrice()
{
return super.getPrice() / 2;
}
public String toString()
{
return super.toString() + "\n(student ID required)";
}
}
Why Each Piece Matters
super(daysInAdvance)is mandatory here, unlike inAdvance.Advanceonly has a one-argument constructor, so Java has no no-argument version to call automatically — leaving this out would fail to compile.super.getPrice() / 2— this callsAdvance's own pricing logic (the $30/$40 rule) and halves that result, rather than repeating the day-count comparison insideStudentAdvance. If the $30/$40 amounts ever changed insideAdvance,StudentAdvancewould automatically charge half of the new amounts with no code changes of its own — exactly what the problem requires.super.toString()inside the override — reusesAdvance's (reallyTicket's) existing"Number: ...\nPrice: ..."formatting instead of retyping it, then appends the one new line this class needs.- The polymorphism underneath all of this:
Ticket.toString()callsgetPrice()onthis. Sincethisis actually aStudentAdvanceobject, that call dispatches toStudentAdvance's overriddengetPrice()— even though the call is physically written insideTicket's code. That's the only reason the printed price comes out halved withoutTicket.toString()ever needing to knowStudentAdvanceexists.
Tracing the Example
Using the question's own sample output for StudentAdvance, "Number: 134\nPrice: 15\n(student ID required)" — which implies the underlying Advance price was $30 (half of it is $15), i.e. purchased 10 or more days ahead:
| Step | Call | Result |
|---|---|---|
| 1 | new StudentAdvance(12) |
super(12) → Advance's price field is set to 30 |
| 2 | .getPrice() |
super.getPrice() returns 30, halved to 15 |
| 3 | .toString() |
super.toString() → Ticket.toString() runs, calling getPrice() on this (a StudentAdvance) → gets 15 → produces "Number: 134\nPrice: 15" |
| 4 | (still in toString()) |
append "\n(student ID required)" → final result "Number: 134\nPrice: 15\n(student ID required)" |
That matches the sample exactly — again, the serial number 134 is whatever Ticket's generator assigned and isn't something this solution computes.
Common Mistakes to Avoid
- Forgetting
super(daysInAdvance)in the constructor. SinceAdvancehas no no-argument constructor, omitting this line is a compile error, not just a logic bug. - Recomputing the $30/$40 rule inside
StudentAdvanceinstead of callingsuper.getPrice(). This would produce the same answer today, but breaks the problem's explicit requirement that a future pricing change inAdvanceshould "just work" forStudentAdvancewith no code modifications. - Rewriting the full ticket text in
toString()instead of callingsuper.toString()first. It's easy to accidentally drop the "Number:" line or introduce a formatting mismatch by retyping it from scratch. - Appending
"(student ID required)"without the leading"\n", which would jam it onto the same line as the price instead of putting it on its own line as the sample shows.
Key Takeaways
- Calling
super.methodName()and building on the result — rather than reimplementing that logic — is what keeps a subclass automatically correct if the parent's rule ever changes. - A subclass only needs an explicit
super(...)call when the parent has no no-argument constructor; otherwise Java inserts one automatically. - A method invoked from inside inherited code still resolves to the most specific override on the actual object at runtime — this is the mechanism that lets one small override (
getPrice()) silently change the behavior of a much bigger inherited method (toString()).