skip to content

How do you implement a Memento in Java so that only the Originator can access the snapshot's state while the Caretaker still cannot read it?

level: middleimportance: should knowfreq 40%

answer

  1. Narrow (marker) interface for Caretaker, wide (private class) for Originator
  2. Enclosing class can read its nested class's privates
  3. private record Snapshot implements public marker Memento
  4. Deep-copy mutable fields into and out of the memento
  5. Immutable memento → use record

basics

~20 s

Make the Memento a private (or private static) nested class of the Originator and expose it to the outside only through a public marker interface. The Originator can read the nested class's private fields; the Caretaker holds it as the marker type and can't see anything.

solid answer

~50 s

The classic Java technique is the narrow/wide interface split using a nested class. Declare the Memento as a `private static` nested class inside the Originator, with private final fields holding the snapshot. The Originator returns it typed as a public empty marker interface (e.g. `Memento`), so the Caretaker only ever holds that opaque type and cannot read the fields. Because Java allows an enclosing class to access its nested class's private members, the Originator's `restore(Memento m)` can cast back to the concrete inner type and read the saved state. Modern Java often uses a `record` for the inner snapshot to get immutability and value semantics for free. You must also handle copying correctly: if any captured field is a mutable object, defensively deep-copy it into the memento (and back out on restore) so later mutations don't corrupt the saved state. This keeps the Originator's representation fully hidden from the Caretaker.

code

java · 24 lines
java
public class Editor {
    private String content = "";
    private int cursor = 0;

    public interface Memento { }                 // narrow: Caretaker sees this
    private record Snapshot(String content, int cursor) implements Memento { } // wide

    public void type(String s) { content += s; cursor = content.length(); }

    public Memento save() { return new Snapshot(content, cursor); }

    public void restore(Memento m) {
        Snapshot s = (Snapshot) m;               // only Editor can read Snapshot
        this.content = s.content();
        this.cursor = s.cursor();
    }
}

// Caretaker: stores opaque mementos, never reads them
class History {
    private final java.util.Deque<Editor.Memento> undo = new java.util.ArrayDeque<>();
    void backup(Editor e) { undo.push(e.save()); }
    void undo(Editor e)   { if (!undo.isEmpty()) e.restore(undo.pop()); }
}

go deeper

for a junior

Knows the memento is a separate snapshot object and that the Caretaker shouldn't read it, even if the exact nested-class mechanism is hazy.

for a middle

Implements the marker-interface + nested-class split correctly and remembers defensive copying for mutable fields.

for a senior

Chooses between field-snapshot and serialization approaches, uses records for immutability, and reasons about aliasing on both save and restore.

for a principal

Sets conventions for snapshot strategy across a codebase, evaluates copy cost vs correctness, and considers versioning/evolution of the memento format when state shape changes over time.

## The core requirement Memento must satisfy two seemingly opposed needs at once: 1. The **Originator** must be able to read everything inside a memento to rebuild itself. 2. The **Caretaker** (and any other outside code) must *not* be able to read the memento's contents — otherwise the Originator's internal representation leaks and encapsulation is broken. Java solves this with the **wide-interface / narrow-interface** idea: give the Originator a *wide* view (full field access) and everyone else a *narrow* view (an opaque type). ## The canonical Java implementation: private nested class + marker interface Key Java language fact: a top-level (enclosing) class and its **nested** class can access each other's **private** members. This is exactly what makes the pattern work cleanly. ```java public class Editor { private String content; private int cursor; // Narrow interface the outside world sees — empty marker. public interface Memento { } // Wide implementation — private, so the Caretaker can't even name the type, // and its fields are invisible outside Editor. private record Snapshot(String content, int cursor) implements Memento { } public Memento save() { return new Snapshot(content, cursor); } public void restore(Memento m) { // Editor can see Snapshot's components because it is nested inside Editor. Snapshot s = (Snapshot) m; this.content = s.content(); this.cursor = s.cursor(); } } ``` What each piece buys you: - **`public interface Memento`** is the *narrow* type. The Caretaker declares `Stack<Editor.Memento>` and can store and return mementos, but the interface has no accessors, so there is nothing to read. - **`private record Snapshot`** is the *wide* type. It is `private`, so outside code cannot even refer to the class, let alone call its accessors. Only `Editor` can cast a `Memento` back to `Snapshot` and read the fields. - Using a **`record`** makes the snapshot immutable with value semantics, so a stored memento can't be mutated after the fact. ## Why a marker interface and not just a public class? If the Memento were a public class with public getters, the Caretaker could read the Originator's state — leaking internals. The empty marker interface is the *minimal* public surface: enough to be a typed reference for storage, but nothing readable. ## Defensive copying: the deep-vs-shallow trap The snapshot must be an *independent* copy. If a field is a **mutable** object (a `List`, a `Date`, an array), copying just the reference means later mutations of the Originator's object also mutate the saved memento — the snapshot is no longer a true snapshot. ```java // BAD: shares the same list reference new Snapshot(this.items); // later edits corrupt the snapshot // GOOD: defensive copy into the memento new Snapshot(List.copyOf(this.items)); ``` On `restore`, copy back out the same way so the restored Originator doesn't alias the memento's internals. ## Alternative: serialization-based memento Instead of hand-copying fields, you can capture state by **serializing** the whole Originator (or a deep clone) into a `byte[]`, and deserialize to restore. This gives an automatic deep copy and needs no per-field code, but requires `Serializable`, is slower, and uses more memory — covered in the serialization-memento question. ## Common mistakes - Making the snapshot type public/readable (leaks state). - Forgetting defensive copies of mutable fields (snapshot silently changes). - Storing mutable mementos that later code can edit (no longer a faithful snapshot — use `record`/immutability).

  • Why does declaring the snapshot as a private nested class let the Originator read it but not the Caretaker?
    Java lets an enclosing class access its nested class's private members, so the Originator can cast and read fields. Because the class is private, no outside code (the Caretaker) can name or access the type; it only holds the public marker interface.
  • When is defensive copying inside the memento unnecessary?
    When all captured fields are immutable (primitives, String, or already-immutable objects). Then sharing the reference is safe because nothing can mutate it later.

saying these in an interview costs you the question

  • Giving the memento public getters (re-exposes internal state)
  • Shallow-copying mutable fields so the snapshot mutates later
  • Making the snapshot class public/non-nested, so the Caretaker can read it
  • Relying on reflection in the Caretaker to read the memento — defeats the pattern

context