NumberGroup / Range: 2015 FRQ 4
A step-by-step solution to the 2015 AP CSA FRQ 4 (NumberGroup and Range), covering designing a Java interface and writing multiple classes that implement it.
Designing an interface from scratch, then writing two different classes that implement it, is the through-line of this AP Computer Science A free-response question — it's less about a single algorithm and more about how Java's interfaces let unrelated classes share one common behavior.
What This FRQ Tests
- AP CSA units: Unit 5 (Writing Classes) and Unit 9 (Inheritance, Interfaces, and Polymorphism)
- Core skill: designing a Java interface with exactly one abstract method, then implementing it in a concrete class
- Secondary skill: writing a method that works with any object of an interface type, without knowing — or needing to know — which concrete class it actually is
- Official category: this year's FRQ 4 leans on interface design and polymorphism (today's Unit 5/Unit 9 territory) rather than the modern FRQ-4 slot's usual "2D Array" content. Pre-2019 AP CSA exams didn't follow today's fixed FRQ-number-to-category order at all, and this question is a clear example of one that simply doesn't map onto the current pattern.
The Setup
- Part (a): design a
NumberGroupinterface from scratch — no starter code is given, just a plain-English description - Part (b): write a
Rangeclass that implementsNumberGroup, representing every integer between a minimum and maximum value, inclusive - Part (c): a
MultipleGroupsclass (not shown in full) already exists with:private List<NumberGroup> groupList— a list of number groups of any kind, initialized in its constructor- You write its
contains(int num)method
Part (a): Designing the NumberGroup Interface
Step-by-Step Approach
- A number group's only defined behavior is answering one question: "does this group contain this integer?" That's the single method the interface needs.
- An interface only declares a method's signature — no body, no instance variables, no constructor.
- Name it to match exactly what any implementing class will need to provide:
contains(int num), returning aboolean.
The Code
public interface NumberGroup
{
boolean contains(int num);
}
Why Each Piece Matters
- An interface method has no
{ }body — just a signature ending in a semicolon. Any class that implementsNumberGroupis responsible for supplying its own version of the logic. - Keeping the interface to exactly one method — as the question explicitly requires — is what lets any class implement it: a range, a hand-picked list of individual numbers, even a mathematical formula could each supply their own
contains, and all of them would still count as aNumberGroup.
Common Mistakes to Avoid
- Giving the method a body (e.g.,
{ return false; }). Interface methods are abstract by default; a body like this doesn't belong here. - Adding extra methods beyond
contains. The question is explicit that the interface must have exactly one method. - Naming the method anything other than exactly
contains. Both later parts of this question depend on calling it by that exact name.
Part (b): Building the Range Class
Step-by-Step Approach
- Declare the class as
implements NumberGroup— Java's way of promising that the class will provide a workingcontainsmethod. - Store the two pieces of state a range needs to remember: its minimum and its maximum value.
- The constructor takes both values as parameters and saves them directly.
contains(int num)simply checks whethernumfalls between the stored minimum and maximum, inclusive on both ends.
The Code
public class Range implements NumberGroup
{
private int min;
private int max;
public Range(int min, int max)
{
this.min = min;
this.max = max;
}
public boolean contains(int num)
{
return num >= min && num <= max;
}
}
Why Each Piece Matters
this.min = min— since the constructor's parameter shares a name with the field it fills,this.minis what tells Java "the field," not "the parameter," on the left-hand side of the assignment.>=and<=, not strict</>— the problem is explicit that a range is inclusive on both ends, so the minimum and maximum values themselves both count as part of the range.Range implements NumberGroupis what makes it legal to store aRangeobject in a variable declared asNumberGroup— exactly as the question's own example shows,NumberGroup range1 = new Range(-3, 2);. This is also what makes Part (c) possible at all.
Tracing the Example
Using the question's own example, new Range(-3, 2) is meant to represent exactly the integers -3, -2, -1, 0, 1, 2:
contains(-3)→-3 >= -3 && -3 <= 2→ true (the minimum itself counts)contains(2)→2 >= -3 && 2 <= 2→ true (the maximum itself counts)contains(-4)→-4 >= -3is false → false (one below the minimum is excluded)contains(3)→3 <= 2is false → false (one above the maximum is excluded)
The question doesn't provide a separate worked table of Range.contains calls on their own (its example table is for MultipleGroups, covered next) — these four checks confirm the boundaries in this solution match the plain-English description exactly.
Part (c): Writing MultipleGroups.contains(int num)
Step-by-Step Approach
groupListcan hold any mix ofNumberGroupobjects —Ranges, or any other class that implements the interface.- Loop through every group currently in the list.
- For each one, call its own
contains(num)— this works no matter which specific class each group actually is, because everyNumberGroupis guaranteed to have this method. - As soon as any single group reports
true, the whole method can returntrueimmediately. - If the loop finishes without any group matching, return
false.
The Code
public boolean contains(int num)
{
for (int i = 0; i < groupList.size(); i++)
{
NumberGroup group = groupList.get(i);
if (group.contains(num))
{
return true;
}
}
return false;
}
Why Each Piece Matters
NumberGroup group = groupList.get(i)— the variable's declared type is the interface, notRangeor any other specific class. This method has no way of knowing, and doesn't need to know, which concrete class each element actually is.group.contains(num)works correctly for any class that implementsNumberGroup— that's the entire point of writing code against an interface instead of a specific class. A brand-newNumberGroup-implementing class added later would work here with zero changes to this method.- Returning
truethe moment one group matches avoids unnecessary checks against the rest of the list.
Tracing the Example
Using the question's own setup — multiple1 holds three ranges: new Range(5, 8), new Range(10, 12), new Range(1, 6):
| Call | Range(5,8) |
Range(10,12) |
Range(1,6) |
Result |
|---|---|---|---|---|
multiple1.contains(2) |
false | false | true | true |
multiple1.contains(9) |
false | false | false | false |
multiple1.contains(6) |
true | (not checked) | (not checked) | true |
All three results match the question's own table exactly, and the last row shows the early-return in action: once Range(5, 8).contains(6) comes back true, the loop never even reaches the other two ranges.
Common Mistakes to Avoid
- Declaring the loop variable as
Rangeinstead ofNumberGroup. This would only compile if every element ofgroupListhappened to actually be aRange, which defeats the entire purpose of storing aList<NumberGroup>in the first place. - Checking only the first group in the list and returning its result directly, instead of looping through all of them.
- Returning
falsefrom inside the loop the first time a single group doesn't match, instead of waiting until every group has had a chance to match.
Key Takeaways
- An interface's entire value is letting code work with "any class that can do X" without caring which specific class it actually is —
MultipleGroups.containsnever once mentionsRangeby name, even thoughRangeobjects are exactly what it ends up checking here. - A class "promises" to implement an interface with the
implementskeyword, and must then actually supply a body for every method the interface declares. - Designing an interface from a plain-English description means finding the one (or few) behaviors every implementing class must share, and leaving everything else — how that behavior actually gets computed — up to each class individually.