Card 04/ 08

GotchaDifficulty: Intermediate1 min

Why Your private List Is Not private

The field is private. The only method that touches it validates carefully. An audit log has entries in it that no call to addEntry ever produced.

java
class Audit {
    private final List<String> entries = new ArrayList<>();

    void addEntry(String line) {
        if (line.isBlank()) { throw new IllegalArgumentException(); }
        entries.add(line);
    }

    List<String> entries() { return entries; }
}
java
audit.entries().add("");
audit.entries().clear();

Line 9 hands out the address of the list. private protected the variable — nothing outside the class can point entries somewhere else — and it never protected the object that variable points at.

Hand out something the caller cannot use to reach back: a copy, or an unmodifiable view.

java
List<String> entries() { return List.copyOf(entries); }

// or, when a snapshot would be too expensive
List<String> entries() { return Collections.unmodifiableList(entries); }

List.copyOf gives the caller their own list, which stays correct even if yours changes afterwards. The unmodifiable view is cheaper and is a window onto yours, so it reflects later changes and refuses to make any.