Prototype — Find the Bug¶
12 buggy snippets across Go, Java, Python.
Bug 1: Returning this Instead of Copy (Java)¶
Symptom: Two "clones" mutate each other.
Fix¶
Lesson¶
Always return a fresh instance.
Bug 2: Shallow Clone of Mutable Field (Java)¶
public class Document {
public List<String> sections;
public Document(Document o) {
this.sections = o.sections; // BUG: shared reference
}
}
Symptom: Mutating clone.sections mutates original.sections.
Fix¶
Lesson¶
Defensive copy mutable collections.
Bug 3: Forgot to Override clone() in Subclass (Java)¶
abstract class Shape { public abstract Shape clone(); }
class Circle extends Shape {
int radius;
public Shape clone() { return new Circle(); } // BUG: doesn't copy radius
}
Symptom: Cloned circles have radius = 0.
Fix¶
public Circle clone() {
Circle c = new Circle();
c.radius = this.radius;
return c;
}
// Or via copy constructor.
Lesson¶
Each subclass must clone its own fields.
Bug 4: Java's Object.clone Skipping Constructor (Java)¶
public class CounterShape implements Cloneable {
public static int count = 0;
public int id;
public CounterShape() { this.id = ++count; }
public Object clone() throws CloneNotSupportedException { return super.clone(); }
}
CounterShape c1 = new CounterShape(); // count=1, id=1
CounterShape c2 = (CounterShape) c1.clone(); // BUG: count still 1, id=1
Symptom: Counter not incremented; c2.id == c1.id.
Fix — copy constructor¶
public CounterShape clone() {
CounterShape c = new CounterShape(); // ctor runs; count++
c.id = this.id; // or generate new id
return c;
}
Lesson¶
Object.clone() skips constructor. Use copy constructor for proper initialization.
Bug 5: Slice Sharing in Go (Go)¶
Symptom: clone.Tags = append(clone.Tags, "x") may mutate original (if capacity allows).
Fix¶
Lesson¶
Go's = is shallow for slices; explicit copy needed.
Bug 6: Map Sharing in Go (Go)¶
Symptom: Setting clone.Flags["x"] = true mutates original.
Fix¶
flags := make(map[string]bool, len(c.Flags))
for k, v := range c.Flags { flags[k] = v }
return &Config{Flags: flags}
Lesson¶
Go maps must be explicitly copied.
Bug 7: Cycle Without Memo in Python (Python)¶
class Node:
def __init__(self, value):
self.value = value
self.neighbors = []
def __deepcopy__(self, memo):
new = Node(self.value)
new.neighbors = [copy.deepcopy(n) for n in self.neighbors] # BUG: no memo
return new
a = Node("A"); b = Node("B")
a.neighbors.append(b); b.neighbors.append(a)
copy.deepcopy(a) # RecursionError
Symptom: Stack overflow on cyclic graphs.
Fix¶
def __deepcopy__(self, memo):
if id(self) in memo: return memo[id(self)]
new = Node(self.value); memo[id(self)] = new
new.neighbors = [copy.deepcopy(n, memo) for n in self.neighbors]
return new
Lesson¶
Use memo to handle cycles.
Bug 8: Cloning External Resources (Java)¶
public class FileWriter implements Cloneable {
public Path path;
public java.io.FileWriter writer; // open file handle
public Object clone() throws CloneNotSupportedException {
return super.clone(); // BUG: shares the file handle
}
}
Symptom: Both clones write to the same file; closing one closes the other; corruption.
Fix¶
public FileWriter clone() throws IOException {
FileWriter fw = new FileWriter();
fw.path = this.path;
fw.writer = new java.io.FileWriter(this.path, true); // fresh handle
return fw;
}
Lesson¶
External resources need fresh instances; don't share.
Bug 9: Cloning Listener Lists (Java)¶
public class Observable implements Cloneable {
private final List<Listener> listeners = new ArrayList<>();
public Object clone() throws CloneNotSupportedException {
return super.clone(); // BUG: shares listeners
}
}
Symptom: Cloning an Observable causes events to fire twice (one for each shared listener entry).
Fix¶
public Observable clone() {
Observable o = new Observable();
// Don't share listeners; let caller re-subscribe.
return o;
}
Or keep listeners separately keyed.
Lesson¶
Cloning callback registrations is usually wrong. Decide intentionally.
Bug 10: Shallow Clone of Date (Java)¶
public class Event implements Cloneable {
public java.util.Date when; // mutable!
public Object clone() throws CloneNotSupportedException {
return super.clone(); // BUG: shared Date
}
}
Symptom: clone.when.setTime(...) mutates original's when.
Fix¶
public Event clone() {
Event e = new Event();
e.when = (Date) this.when.clone(); // Date is Cloneable + mutable
return e;
}
Or use Instant (immutable) and skip the deep clone.
Lesson¶
Mutable types in fields require explicit deep copy. Prefer immutable types (Instant, LocalDateTime).
Bug 11: Prototype Registry Race (Go)¶
type Registry struct {
protos map[string]Shape // BUG: no synchronization
}
func (r *Registry) Register(name string, p Shape) { r.protos[name] = p }
func (r *Registry) Create(name string) Shape { return r.protos[name].Clone() }
Symptom: go test -race reports race; potential panic on concurrent map write.
Fix¶
type Registry struct {
protos map[string]Shape
mu sync.RWMutex
}
func (r *Registry) Register(name string, p Shape) { r.mu.Lock(); defer r.mu.Unlock(); r.protos[name] = p }
func (r *Registry) Create(name string) Shape { r.mu.RLock(); defer r.mu.RUnlock(); return r.protos[name].Clone() }
Lesson¶
Concurrent registries need locks (or sync.Map).
Bug 12: Cloning Singleton (Java)¶
public final class Logger implements Cloneable {
private static final Logger INSTANCE = new Logger();
private Logger() {}
public static Logger getInstance() { return INSTANCE; }
public Object clone() throws CloneNotSupportedException {
return super.clone(); // BUG: creates a second Logger
}
}
Symptom: Logger.getInstance().clone() returns a different Logger; singleton invariant broken.
Fix¶
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Singleton cannot be cloned");
}
Lesson¶
Singletons must explicitly forbid cloning.
Practice Tips¶
- Run
go test -racefor any Go Prototype code. - Test mutation independence — mutate clone, assert original unchanged.
- Test cycle handling — clone cyclic graphs.
- Test resource handling — verify fresh handles, not shared.
- Use
final/ immutable types to avoid the deep-clone problem entirely.
← Tasks · Creational · Roadmap · Next: Optimize