Cancellation rules and safe copies
1.8.2 — Cancellation rules and safe copies
This lesson adds a rule to an attendance report: cancelled bookings contribute zero participants. It also fixes a report that accidentally shares an array with its caller.
A boolean holds true or false. A method can use if to choose a result. An array stores several values; assigning an array variable copies its reference.
Each code cell is a complete program with its own classes and inputs. Java starts in main; the statements inside its braces run in order. The other class declarations describe the objects used by main. Run each cell as a whole.
Give a cancelled booking a count of zero
A Booking starts with cancelled set to false. cancel changes that field to true. activeParticipants returns 0 for a cancelled booking and the original count otherwise.
The stored participant count is kept, so cancellation changes the reporting rule without erasing the original value.
Use the rule for every booking
The total method loops through a Booking array. For each object, it asks activeParticipants for the count to add. It does not repeat the cancellation test.
The next example cancels the second booking. Only the first booking’s two participants contribute to the total.
Keep the rule in one method
Two reports can call the same activeParticipants method. If the cancellation rule changes, its logic has one place to change.
The detail print below and attendanceTotal both use that method. Cancelling an already cancelled booking has the same result: it remains cancelled.
Find an array that is shared by mistake
The next AttendanceReport is meant to remember the counts supplied at construction. But this.counts = counts; stores the caller’s array reference. Both references identify the same array.
Changing input[0] afterward changes the report’s total too. This example demonstrates the bug; it finishes normally.
Copy the input when storing it
For an int array, clone creates a separate array with the same values. Storing that copy prevents later caller edits from changing this report. This is a defensive copy.
final prevents assigning a different reference to counts; it does not make the array elements unchangeable. The copy is what separates the two arrays.
The constructor below also checks that every stored count is nonnegative.
Return a copy too
A getter that returns the stored array would let callers modify its elements. The countsCopy method returns another clone, so edits to the returned array leave the report unchanged.
This copy approach works for the int arrays here. Copying an array of objects copies its references, not the objects themselves.
Check that the rules still hold
After a change, run older cases as well as the new ones. These are regression checks. The next cell checks an active booking, a cancelled booking, an empty report, and a caller edit.
The first three lines check cancellation. The last checks that a stored attendance array stays separate from its input. Adding the copy should not change the cancellation results.
Practice: keep the original counts
The constructor below still shares the input array. Replace this.counts = counts; with this.counts = counts.clone();. Leave main unchanged.
The input is edited after construction. The completed report should still print 5.
Hint
Add .clone() to counts on the right of the constructor assignment. You are copying the input, not changing the total calculation.
Worked solution
public class PracticeReport {
public static void main(String[] args) {
int[] input = {2, 3};
AttendanceReport report = new AttendanceReport(input);
input[0] = 9;
System.out.println(report.total());
}
}
class AttendanceReport {
private final int[] counts;
AttendanceReport(int[] counts) {
this.counts = counts.clone();
}
long total() {
long total = 0;
for (int count : counts) {
total = total + count;
}
return total;
}
}Try other starting arrays
In your completed cell, change only the initial values in input:
| Initial input | Expected report total after input[0] = 9 |
|---|---|
{0} | 0 |
{4} | 4 |
{2, 3} | 5 |
For an empty-array check, use {} and delete the input[0] assignment because index 0 does not exist. The report total should be 0.
How much work does total do?
The method visits every count once. Twice as many counts means roughly twice as many loop repetitions. The copy also visits the array’s elements. Both grow in proportion to the number of counts.
Enable JavaScript to run the Java notebook.