Card 04/ 08

GotchaDifficulty: Intermediate1 min

Why a private List Returned by a Getter Is Not private

The only method that adds to the log validates carefully, and the log has entries it never added.

private stops other code naming a field; it does not protect the object the field points at. A getter that returns a private list hands out its address, and the caller can then change the list without any of the class's methods running. Return a copy or an unmodifiable view instead.

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, since 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.