Card 05/ 08
All 8 cards
GotchaDifficulty: Advanced1 min
Why the Same Leak Happens Through a Constructor
Every getter now returns a copy. The audit log still gains entries nobody added, and this time the class never hands anything out at all.
class Audit {
private final List<String> entries;
Audit(List<String> initial) {
this.entries = initial;
}
List<String> entries() { return List.copyOf(entries); }
}List<String> mine = new ArrayList<>();
Audit audit = new Audit(mine);
mine.add(""); // and the audit has it tooLine 5 stored the address it was handed. The caller kept their own copy of that address, so both the caller and the object now reach one list — and the validation in addEntry never sees anything the caller does directly.
Put this beside the getter and the shared fact is the whole lesson: private protects the variable, never the object it points at, and it does not matter which direction the reference crossed the boundary. Out through a getter or in through a constructor, the result is two names for one object.
Audit(List<String> initial) {
this.entries = new ArrayList<>(initial);
}None of this applies to a field holding a String, an int or any other immutable value. There is nothing to reach back into, which is why immutable types make encapsulation cheap.