The need to copy objects come up frequently in object-oriented programming. The are many times when an object needs to be copied, so a new object with the same state can be modified.
There are several solutions for this. Which one is best? That's, unfortunately, debatable.
Copy Constructors Considered Harmful
The reasoning behind this statement is fairly simple. I'll, uh, borrow an example found on the web.
public class Dog { public int age; public int weight; public Dog() { } public Dog(Dog original) { age = original.age; weight = original.weight; } }
Fairly simple, right? Dog(Dog)
is a copy constructor - it creates a copy of the original Dog
object when invoked. What's the problem? Well, let's throw in another class:
public class Dalmatian extends Dog { public int spots; }
Using the Dog(Dog)
copy constructor would strip a Dalmatian
object of its spots, turning it into a plain-old Dog
object. Clearly, this is not desired. This brings us to clone()
.
clone()
Considered Harmful
clone()
becomes the obvious solution. clone()
will return an exact clone, so a Dalmatian
will remain a Dalmatian
.
Well, might. clone()
doesn't actually guarantee that - it just returns an Object
and the documentation is fairly clear that, while it's suggested that it's of the same class as the original, it's not a requirement.
But even assuming it did, there's another problem, involving final
objects.
The examples so far have been mutable objects. There's a good reason for this: there's absolutely no reason to clone immutable objects. (And if you've managed to come up with a reason, I don't want to see your code.)
So let's update the Dog
class to add a List
of names of the dog:
public class Dog implements Cloneable { public final List<String> names = new ArrayList<String>(); public int age; public int weight; public Dog clone() { try { return (Dog) super.clone(); } catch (CloneNotSupportedException e) { throw new Error("Is too"); } } }
The List
is final
because it only needs to be created at construction time, and may never be null
. So let's suppose we want to clone()
a Dog
:
Dog bowser = new Dog(); bowser.names.add("Fido"); Dog bobBarker = bowser.clone(); bowser.names.add("Bowser"); bobBarker.names.add("Bob Barker");
See the problem? The problem is that clone()
is a shallow copy, so the same List<String>
is used in both objects. This means that both bowser
and bobBarker
have three names: Fido, Bowser, and Bob Barker. This wasn't what was intended. So how about:
public class Dog implements Cloneable { public final List<String> names = new ArrayList<String>(); public int age; public int weight; public Dog clone() { try { Dog clonedDog = (Dog) super.clone(); clonedDog.names = new ArrayList<String>(names); } catch (CloneNotSupportedException e) { throw new Error("Is too"); } } }
Of course, that won't compile, because it attempts to reassign names
, which is final
. Oops.
So What's The Right Answer?
Good question. The general answer is that there isn't really one.
I've glossed over other issues with the copy constructor (namely that as members get added and deleted, the copy constructor has to manually be kept in check). However, for final
classes, a copy constructor is a fine solution.
For classes that are not final
than a solution similar to clone()
is a better idea. If clone()
is usable directly, great! If it isn't, due to final
issues, then something more like the following may work better:
public class Dog { public final List<String> names; public int age; public int weight; public Dog() { names = new ArrayList<String>(); } protected Dog(Dog original) { names = new ArrayList<String>(original.names); age = original.age; weight = original.weight; } public Dog copy() { return new Dog(this); } } public class Dalmatian extends Dog { public int spots; public Dalmatian() { } protected Dalmatian(Dalmatian original) { super(original); spots = original.spots; } public Dalmatian copy() { return new Dalmation(this); } }
Of course, that solution requires every subclass to properly implement both copy()
and the copy constructor, which must properly call super(Dog original)
.
But it solves the copy constructor problem and the clone()
problem, although it unfortunately is likely to be slower than clone()
.
Oh well.
Thank you
I am currently enrolled in a university computer science course in which we use Java to program.
Due to confusion and ambiguity in our lecture material, I have been searching the internet extensively for good, clear examples of copy constructor and clone() usage.
I know this is an older post, but it is the first -- and so far, only -- good example I have found. Thank you so much!