CompSci.rocks
FRQcsapa

CombinedTable: 2021 FRQ 2

A step-by-step solution to the 2021 AP CSA FRQ 2 (CombinedTable), covering object composition, storing object references, and computing values that update automatically in Java.

Two restaurant tables pushed together form the scenario behind this AP Computer Science A free-response question — you build a brand-new class that wraps two existing objects and computes values that stay in sync even after those objects change.

What This FRQ Tests

  • AP CSA units: Unit 5 (Writing Classes) — this one leans on composition (a class that stores other objects as fields) rather than inheritance
  • Core skill: writing a complete class from scratch, including its fields, constructor, and every method the prompt describes
  • Secondary skill: recognizing that storing a reference to an object (instead of copying its data out at construction time) is what makes computed values automatically stay current
  • Official category: "Classes" — always FRQ 2 on the AP CSA exam

The Setup

  • The given SingleTable class has:
    • int getNumSeats() — always 4 or more
    • int getHeight() — the table's height in centimeters
    • double getViewQuality() — the view quality, which can change after the object is created
    • void setViewQuality(double value) — changes the view quality (already written, not part of this problem)
  • You're asked to write the entire CombinedTable class, which represents two SingleTable objects pushed together:
    • A constructor: CombinedTable(SingleTable t1, SingleTable t2)
    • boolean canSeat(int numCustomers) — can this combined table seat that many people?
    • double getDesirability() — how desirable is this combined table?
  • The rules:
    1. A CombinedTable seats two fewer people than the sum of its two tables' seat counts (seats are lost when the tables are pushed together).
    2. If the two tables are the same height, desirability is the average of their view qualities.
    3. If the two tables are different heights, desirability is that same average, minus 10.

Building the CombinedTable Class

Step-by-Step Approach

  1. Declare two private SingleTable fields to hold the two tables being combined.
  2. Write a constructor that takes both SingleTable objects as parameters and simply stores them — don't pull out and copy their seat counts, heights, or view qualities into separate fields.
  3. Write canSeat(int numCustomers): add the two tables' seat counts together, subtract 2, and check whether numCustomers is less than or equal to that total.
  4. Write getDesirability(): compute the average of the two tables' current view qualities, then check whether the two tables' heights are equal to decide whether to subtract 10.

The Code

public class CombinedTable
{
    private SingleTable table1;
    private SingleTable table2;

    public CombinedTable(SingleTable t1, SingleTable t2)
    {
        table1 = t1;
        table2 = t2;
    }

    public boolean canSeat(int numCustomers)
    {
        int totalSeats = table1.getNumSeats() + table2.getNumSeats() - 2;
        return numCustomers <= totalSeats;
    }

    public double getDesirability()
    {
        double average = (table1.getViewQuality() + table2.getViewQuality()) / 2;

        if (table1.getHeight() == table2.getHeight())
        {
            return average;
        }
        else
        {
            return average - 10;
        }
    }
}

Why Each Piece Matters

  • Storing table1 and table2 as fields, not copying their data out — this is the single most important decision in this problem. Because table1 and table2 hold references to the actual SingleTable objects, calling table1.getViewQuality() inside getDesirability() always reads whatever that table's view quality is right now — including changes made through setViewQuality() after the CombinedTable was constructed.
  • numCustomers <= totalSeats, not < — the rule says a table "can seat" a given number of customers, which includes seating exactly that many.
  • Dividing by 2 for the average — both getViewQuality() calls return double, so this division is already floating-point; no cast is needed here (unlike problems that start from int data).
  • == for comparing heightsgetHeight() returns a primitive int, and primitives are always compared with ==, never .equals(). (Contrast this with comparing String objects, which always use .equals().)

Tracing the Example

Given from the question:

  • t1: 4 seats, view quality 60.0, height 74
  • t2: 8 seats, view quality 70.0, height 74
  • t3: 12 seats, view quality 75.0, height 76

Trace through each call:

Call Result Why
c1 = new CombinedTable(t1, t2) c1 stores references to t1 and t2
c1.canSeat(9) true total seats = 4 + 8 − 2 = 10; 9 ≤ 10
c1.canSeat(11) false 11 > 10
c1.getDesirability() 65.0 heights equal (74 = 74) → average of 60.0 and 70.0
c2 = new CombinedTable(t2, t3) c2 stores references to t2 and t3
c2.canSeat(18) true total seats = 8 + 12 − 2 = 18; 18 ≤ 18
c2.getDesirability() 62.5 heights differ (74 ≠ 76) → average of 70.0 and 75.0 (72.5), minus 10
t2.setViewQuality(80) changes t2's view quality directly
c2.getDesirability() 67.5 average of 80.0 and 75.0 (77.5), minus 10

That last row is the whole point of the problem: c2 was never told about the change to t2 — it just re-reads t2.getViewQuality() every time getDesirability() is called, because it stored a reference to t2 itself rather than a snapshot of its data.

Common Mistakes to Avoid

  • Copying getViewQuality(), getHeight(), or getNumSeats() into new fields inside the constructor (e.g., viewQuality1 = t1.getViewQuality();). This "freezes" the data at construction time — c2.getDesirability() would keep returning 62.5 forever, even after t2.setViewQuality(80) is called, which contradicts the example.
  • Forgetting the - 2 seat adjustment, or subtracting it from the wrong place (e.g., inside getNumSeats(), which isn't yours to modify).
  • Applying the -10 penalty when heights are equal instead of when they differ — double check which branch of the if matches which rule.
  • Using .equals() to compare int heights. .equals() is for objects like String; primitives like int compare with ==.

Key Takeaways

  • When a new class is built out of existing objects, store references to those objects as fields — don't copy their data into new fields, or the new class will silently go stale whenever the original objects change.
  • A method that reports on other objects' current state should call their getters inside itself, every time it runs, rather than caching a result.
  • Primitive values (int, double, boolean) compare with ==; object values like String compare with .equals().

Related FRQs