Defensive Copying in Java
Defensive copying protects an object's internal state by creating independent copies of mutable collections when data crosses an API boundary. It prevents callers from changing an object's state through a reference they still control.
Java objects hold references to collection objects, so assigning a collection reference does not create a new collection. If a class stores a caller-provided `List`, later changes to that list can unexpectedly change the class's internal state.
A defensive copy creates a new collection containing the same elements. For example, `new ArrayList<>(input)` gives the object its own list structure, so adding or removing elements from `input` no longer changes the stored collection.
Defensive copying can also be needed when returning internal mutable state. Returning the actual internal list exposes it to callers, while returning `new ArrayList<>(internalList)` gives the caller an independent collection. The copy protects the collection structure, but it does not automatically clone mutable objects stored inside the collection.
For stronger read-only APIs, Java also provides unmodifiable views and immutable collection factories. An unmodifiable view prevents mutation through that particular reference but may still reflect changes made elsewhere; an actual defensive copy has independent collection structure.
Key points
- Copy mutable collections when accepting them across an API boundary.
- Copy mutable collections when returning internal state.
- `new ArrayList<>(list)` copies the collection structure, not necessarily its elements.
- Defensive copying prevents aliasing between the object's state and external collection references.
- An unmodifiable view is different from an independent defensive copy.
Interview questions
Does `new ArrayList<>(list)` perform a deep copy?
No. It creates a new list structure, but the elements are normally the same object references as those in the source list. This is therefore a shallow copy of the collection. If the elements themselves are mutable and must be isolated, they need an appropriate copying strategy too.
Why is defensive copying needed when a constructor accepts a mutable `List`?
A Java collection variable holds a reference to the collection object. If the constructor stores that reference directly, the caller can later mutate the list and thereby change the object's internal state. Creating a new collection breaks that alias. This gives the object control over its collection structure.
Why should a getter sometimes return a defensive copy?
Returning an internal mutable collection directly allows callers to mutate the object's state without going through its API. Returning a copy gives the caller a separate collection that it can modify safely. The object's original collection remains unchanged.
What is the difference between defensive copying and `Collections.unmodifiableList`?
A defensive copy creates a separate collection, while an unmodifiable list is typically a read-only view over an existing collection. A view prevents mutation through the returned reference but can still observe mutations made through another reference to the backing collection. A defensive copy provides a snapshot of the collection structure at the time of copying.