Implement Prototype in Java with explicit copy semantics first, then use Cloneable only when an API or legacy model truly requires it.
Prototype: A pattern where a preconfigured object produces new instances by copying its current state instead of rebuilding from scratch.
In Java, Prototype is less about the clone() method than older tutorials suggest. The real design problem is how to create a new object from an existing one without accidentally sharing mutable state or hiding important construction rules.
The cleanest modern Java implementation is often a copy constructor or named copy factory:
1public final class ReportTemplate {
2 private final String title;
3 private final List<String> sections;
4 private final Map<String, String> defaults;
5
6 public ReportTemplate(String title,
7 List<String> sections,
8 Map<String, String> defaults) {
9 this.title = title;
10 this.sections = List.copyOf(sections);
11 this.defaults = Map.copyOf(defaults);
12 }
13
14 public ReportTemplate(ReportTemplate source) {
15 this(source.title, source.sections, source.defaults);
16 }
17
18 public ReportTemplate copyWithTitle(String newTitle) {
19 return new ReportTemplate(newTitle, sections, defaults);
20 }
21}
This is still Prototype. The prototype is the existing ReportTemplate. What matters is that the copy rule is explicit and safe.
clone() Is Not The Default RecommendationJava supports Cloneable, but it comes with awkward semantics:
Cloneable is a marker interface with no explicit copy contractObject.clone() is shallow unless you do more workThat does not make clone() unusable. It means the design should justify it. In most application code, explicit copy logic is clearer than inheriting clone() conventions.
The important decision is what the copy owns.
If the object only contains immutable values, copying can be cheap and straightforward. If it contains collections, caches, file handles, sockets, locks, or references to shared services, then “copying the object” may be the wrong operation entirely.
Ask:
A shallow copy duplicates the top-level object while keeping references to nested objects. A deep copy duplicates the nested graph as well. Neither is automatically correct.
Shallow copy is reasonable when nested state is immutable or intentionally shared. Deep copy is necessary when the new object must evolve independently.
1public final class OrderDraft {
2 private final Customer customer;
3 private final List<LineItem> items;
4
5 public OrderDraft(OrderDraft source) {
6 this.customer = source.customer; // shared immutable reference
7 this.items = source.items.stream()
8 .map(LineItem::copy)
9 .toList();
10 }
11}
The copy policy is visible here. That is the goal.
| Approach | Good fit | Main caveat |
|---|---|---|
| Copy constructor | Most application-domain types | Can get verbose for large object graphs |
Static copy factory such as from(existing) | Named or role-specific copies | Still needs explicit field policy |
clone() / Cloneable | Legacy APIs, frameworks, or established inheritance trees | Easy to hide shallow-copy bugs |
| Builder seeded from an existing object | Many optional changes during copy | More moving parts than a direct copy |
For a deeper comparison, see Copy Constructors and Cloning Alternatives.
Prototype is useful when:
Good examples include document templates, preconfigured request objects, workflow drafts, test fixtures, and game or UI templates.
Prototype is a weak choice when:
If readers cannot quickly answer what is copied, shared, and reset, the pattern is probably the wrong abstraction.
This page keeps the implementation shape self-contained, then pushes detail outward:
When reviewing a Prototype implementation in Java, ask:
clone()?Prototype is helpful when the copy boundary is part of the design. It is dangerous when copying is treated as a shortcut around thinking about state ownership.