Defensive Copying (Java)

Context: FIT2099_MOC · protect encapsulation when a class holds a mutable object · stop callers reaching in through a shared reference Problem it solves: a getter/setter/constructor that passes a mutable private object by reference, letting outside code mutate the class’s internal state.

Quick Revision

  • 🎯 Trigger: a getter returns a reference to a mutable private field (or a setter/constructor stores the caller’s object directly) ➔ copy it.
  • ⚡ Key Constraint: the leak is caused by aliasing — two references to the same heap object; the caller’s alias mutates the class’s private state, silently breaking encapsulation.

🔧 Minimal Working Example

// PRIVACY LEAK: getFuel() hands out the internal Fraction by reference
public class GasTank {
    private Fraction fuel = new Fraction(1, 1);
    public Fraction getFuel() { return fuel; }              // leak!
}
// caller mutates the tank's private field through the alias:
GasTank tank = new GasTank();
Fraction fuelRead = tank.getFuel();   // same object as tank.fuel
fuelRead.setNumerator(2);             // tank.fuel is now changed from OUTSIDE
 
// FIX: defensive copy — return a NEW object
public Fraction getFuel() { return new Fraction(fuel); }    // caller gets a copy

Expected output: with the leak, mutating fuelRead corrupts tank.fuel; with the copy, changes to the returned Fraction leave the original untouched.

  • Mutable vs immutable ➔ mutable objects can change after construction (StringBuilder, Date, most of your own classes); immutable can’t (String, Integer) — only mutable fields need defending.
  • Where to copy ➔ any time a mutable object crosses the class boundary: out of a getter, into a setter, and into a constructor.
  • Why ➔ a private field should be changed only by its own class; a shared reference hands that control away.

✍️ Practice

⚠️ Common Mistakes

  • 💡 Only mutable objects leak ➔ returning a String/int is safe (immutable/value); a Date, array, List, or your own settable class is not.
  • 💡 Copy at BOTH ends ➔ a defensive getter alone still leaks if the constructor/setter stored the caller’s original object — defend every crossing.
  • 💡 Reduces connascence ➔ a leaked reference creates hidden coupling (the caller’s changes reach your internals); copying keeps the class in sole control of its state.