skip to content

Where does the Prototype idea appear in the JDK, and how would you decide between Prototype/clone, a copy constructor, and a factory in a real design?

level: principalimportance: nice to knowfreq 38%

answer

  1. Array.clone() + collection clone()/copy constructors in JDK
  2. Prototype's edge: copy without knowing the concrete type
  3. Registry of exemplars you duplicate
  4. Decision: array→clone, known type→copy ctor, polymorphic→own copy()
  5. Don't expose Cloneable; own a copy() interface

basics

~20 s

Java's array clone() and types like ArrayList.clone() show the Prototype idea of copying. To choose, prefer copy constructors/factories for normal objects, use array clone() for arrays, and reach for true Prototype when you must copy objects whose concrete type you don't know at compile time.

solid answer

~40 s

The Prototype idea surfaces in the JDK mainly as cloning: arrays have a built-in clone(), and collections like ArrayList, HashMap, and HashSet implement Cloneable with public clone() overrides. The pattern's real value is *copying when the concrete type is unknown* — you hold a reference to some abstract type, call copy() on it, and get the right subtype back without a switch on the class. In day-to-day design, prefer copy constructors or copy factories: they run invariants, support final fields, and allow conversion. Use array clone() because it's idiomatic and well-typed. Reserve a genuine Prototype/clone abstraction for frameworks where you register exemplar objects and duplicate whichever the caller selects, or where deep-copying a polymorphic object graph through a uniform interface matters more than clone()'s warts.

code

java · 16 lines
java
// Prototype done right: own copy() interface, polymorphic, backed by copy ctors
interface Shape {
    Shape copy();           // not Cloneable; returns the right subtype
    double area();
}

final class Circle implements Shape {
    private final double r;
    Circle(double r) { this.r = r; }
    Circle(Circle o) { this.r = o.r; }      // copy constructor (final-friendly)
    public Circle copy() { return new Circle(this); }  // covariant return
    public double area() { return Math.PI * r * r; }
}

// Caller copies without knowing the concrete type:
Shape duplicate(Shape s) { return s.copy(); }

go deeper

for a junior

Recognizes that arrays and collections offer copying and that copy constructors exist as an alternative to clone().

for a middle

Can pick copy constructors for known types and array clone() for arrays, and explain why.

for a senior

Identifies Prototype's polymorphic-copy advantage, cites JDK examples, and justifies copy constructors as the default while reserving Prototype for unknown-type copying.

for a principal

Drives a coherent copy strategy across a codebase/API (own copy() interface over Cloneable, exemplar registries where construction is costly, conversion constructors), weighing coupling, immutability, extensibility, and performance trade-offs.

## The Prototype idea inside the JDK 'Prototype' = produce a new object by **copying an existing one** rather than constructing it. The JDK uses this idea in a few places: - **Arrays:** every array type has a public `clone()` that returns a (shallow) copy of the array. This is the *one* place `clone()` is genuinely idiomatic and recommended. - **Collections:** `ArrayList`, `LinkedList`, `HashMap`, `HashSet`, `TreeMap`, `Calendar`, `Date`, etc. implement `Cloneable` and override `clone()` publicly (mostly shallow — the elements/keys/values are shared). Most modern code instead uses their **copy constructors** (`new ArrayList<>(other)`, `new HashMap<>(other)`), which are also conversion constructors accepting any `Collection`/`Map`. - **`Object.clone()`** itself is the engine all of the above build on. ## What Prototype is uniquely good at The distinctive power of Prototype over a factory or constructor: **copying an object whose concrete class you don't know at compile time.** Suppose you have `Shape s` and want a duplicate. With constructors you'd have to know whether it's a `Circle` or `Polygon` and call the right one. With Prototype you just call `s.copy()` and polymorphism returns the correct subtype. This is why Prototype shines in: - **Registries of exemplars:** keep a map of pre-configured prototype objects (e.g. enemy types in a game, pre-styled document elements) and `copy()` whichever the caller names — no giant `switch` over types, and new types register themselves. - **Frameworks that duplicate user-supplied objects** through a uniform interface (e.g. a graph/AST node you must deep-duplicate without knowing the node subtype). ## A decision framework Ask, in order: 1. **Is it an array?** Use `array.clone()`. Idiomatic, well-typed, no reason to do otherwise. 2. **Do you know the concrete type at the copy site?** If yes, use a **copy constructor** (`new T(other)`) or **copy factory** (`T.copyOf(other)`). These run invariants, work with `final` fields, avoid the checked exception, and can convert types. This is the default for ~all ordinary value/entity objects. 3. **Must you copy through an abstract type without knowing the subtype?** Define your *own* `copy()` method on the abstraction (NOT `Object.clone()`), implemented per subtype, ideally backed by each subtype's copy constructor. This gives you Prototype's polymorphism without `Cloneable`'s flaws. Return a covariant type where useful. 4. **Is construction genuinely expensive and you want pre-built templates?** A prototype **registry** of exemplars you duplicate is the right shape — again with a clean `copy()` interface, not `clone()`. 5. **Arbitrary cyclic graph, copy correctness over speed?** Consider serialization-based deep copy or a dedicated copier with an identity map. ## Trade-offs to articulate at a senior/principal level - **Coupling:** constructors/factories couple the caller to the concrete type; Prototype decouples it (caller depends only on the abstraction). That decoupling is the whole point when types are open/extensible (plugins). - **Invariants & immutability:** copy constructors win because they reuse normal construction and tolerate `final`. A custom `copy()` backed by copy constructors keeps both benefits *and* polymorphism. - **Performance:** copying a fully-built exemplar can beat re-running expensive construction — the original motivation for Prototype — but measure; for cheap objects it's premature. - **API surface:** exposing `clone()`/`Cloneable` leaks a fragile contract to subclasses and callers; a narrow `copy()` interface you own is cleaner and documents intent. ## Bottom line The Prototype *pattern* earns its place when **polymorphic copying** or **exemplar duplication** is the requirement; implement it with your own `copy()` method (often delegating to copy constructors), not with `Cloneable`. Use `Object.clone()` essentially only for arrays. For everything else, copy constructors/factories are the simplest correct default. ## Key terms - **Exemplar/prototype registry:** a collection of pre-configured template instances you duplicate on demand. - **Polymorphic copy:** copying via an abstract type so the correct concrete subtype is produced without the caller knowing it. - **Conversion constructor:** a constructor accepting a different (often super) type, e.g. `new ArrayList<>(anyCollection)`. - **Identity map:** a map keyed by object identity used during deep copy to avoid re-copying / infinite loops on shared or cyclic references.

  • When does Prototype clearly beat a factory method?
    When you must copy an object whose concrete subtype is unknown at the call site, or when duplicating expensive pre-built exemplars from a registry. A factory needs to know which type to build; Prototype delegates that to the object itself via a polymorphic copy().
  • Why is array.clone() an acceptable use of clone() when most others aren't?
    Arrays have a public, correctly-typed clone() with no inheritance/final-field/invariant complications; it's concise and idiomatic. The flaws of Cloneable mostly bite custom class hierarchies, not the flat array case.

Choosing the copy mechanism is like choosing how to reproduce a document: photocopy a single sheet (array clone), retype from a known template (copy constructor), or hand any document to a smart scanner that figures out its format and reproduces it faithfully (polymorphic Prototype copy).

saying these in an interview costs you the question

  • Defaulting to Cloneable/clone() for general objects instead of copy constructors
  • Claiming JDK collections deep-copy on clone() (they're shallow)
  • Missing that Prototype's main value is polymorphic/unknown-type copying
  • Exposing Cloneable on a public API instead of a narrow copy() method you control

context