CompSci.rocks
FRQcsapa

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 NumberGroup interface from scratch — no starter code is given, just a plain-English description
  • Part (b): write a Range class that implements NumberGroup, representing every integer between a minimum and maximum value, inclusive
  • Part (c): a MultipleGroups class (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

  1. 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.
  2. An interface only declares a method's signature — no body, no instance variables, no constructor.
  3. Name it to match exactly what any implementing class will need to provide: contains(int num), returning a boolean.

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 implements NumberGroup is 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 a NumberGroup.

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

  1. Declare the class as implements NumberGroup — Java's way of promising that the class will provide a working contains method.
  2. Store the two pieces of state a range needs to remember: its minimum and its maximum value.
  3. The constructor takes both values as parameters and saves them directly.
  4. contains(int num) simply checks whether num falls 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.min is 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 NumberGroup is what makes it legal to store a Range object in a variable declared as NumberGroup — 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 <= 2true (the minimum itself counts)
  • contains(2)2 >= -3 && 2 <= 2true (the maximum itself counts)
  • contains(-4)-4 >= -3 is false → false (one below the minimum is excluded)
  • contains(3)3 <= 2 is 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

  1. groupList can hold any mix of NumberGroup objects — Ranges, or any other class that implements the interface.
  2. Loop through every group currently in the list.
  3. For each one, call its own contains(num) — this works no matter which specific class each group actually is, because every NumberGroup is guaranteed to have this method.
  4. As soon as any single group reports true, the whole method can return true immediately.
  5. 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, not Range or 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 implements NumberGroup — that's the entire point of writing code against an interface instead of a specific class. A brand-new NumberGroup-implementing class added later would work here with zero changes to this method.
  • Returning true the 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 Range instead of NumberGroup. This would only compile if every element of groupList happened to actually be a Range, which defeats the entire purpose of storing a List<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 false from 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.contains never once mentions Range by name, even though Range objects are exactly what it ends up checking here.
  • A class "promises" to implement an interface with the implements keyword, 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.

Related FRQs