Mark said:
Where I think copy constructors are better:
1. Semantics better defined that clone().
2. Able to use final fields which hold objects with internal state. (In
other words, if you need to do a deep copy with clone, you can't use a
final field for that variable. With a copy constructor, you can use a
final field just fine.)
3. Easier for programmers to understand and get right.
This doesn't mean copy constructors are always better, but you should
consider them. If your design can make do with just copy-ctors and
forgo clone(), then you probably should.
Why not have your cake and eat it too?
private (or whatever) Foo (Foo toCopy) {
this.fieldX = toCopy.fieldX;
this.fieldY = toCopy.fieldY;
this.fieldZ = toCopy.fieldZ.clone();
this.fieldW = new Bar(toCopy.fieldW);
}
public Foo clone () {
return new Foo(this);
}
Doesn't work to properly copy Foo subclasses though.
But maybe this does:
public interface Copier<T> {
T getCopy (T object);
}
public abstract class Foo<T extends Foo<T>> {
private Copier<T> copier;
protected Foo (Copier<T> copier) {
this.copier = copier;
}
@SuppressWarnings("unchecked");
public T clone () {
return copier.getCopy((T)this);
}
}
public class Bar extends Foo<Bar> {
private int field;
public Bar (int x) {
super(new Copier<Bar> () {
public Bar copy (Bar object) {
return new Bar(object.field);
}
});
field = x;
}
public int getX () {
return field;
}
}
One "curiously recurring template pattern", one
@SuppressWarnings("unchecked"), and one subclass that can copy itself.
Of course, this may still run into problems if you make subclasses of
subclasses. At least the type parameters can get hairy. Even then you
could just dump the generics and leave clone to return Object and
callers to cast.
public abstract class Baz <T extends Baz> extends Foo<T>, perhaps?
I seem to remember this kind of thing eventually confusing the compiler.
The 1.5 compiler anyway. Maybe 1.6 is better able to deal with this sort
of thing.
Oh, and as for someone else's suggestion of a ValueCopier, you could
make a generic ValueCopier for everything Serializable. Simplest is to
use File.createTemporaryFile, ObjectOutputStream, and ObjectInputStream.
Disk I/O is avoided by using ByteArrayOutputStream wrapped with
ObjectOutputStream, along with ByteArrayInputStream. It might also be
possible to use nio channels to create a sort of pipe. The lack of
multiple inheritance in Java makes it tricky but you can make a class
that contains an ArrayList<Byte> and has getInputStream and
getOutputStream methods that furnish inner class instances, one which
appends to the ArrayList at one end, and the other that consumes from
the other end and blocks if need be. You'd need to fiddle around with
synchronized, wait, and notify, and might want to use ArrayDeque instead
of ArrayList. The advantage over ByteArray streams is in memory use if
copying a very large object, assuming your ValueCopier<Serializable>
spawned a parallel thread to writeObject while the main thread did the
readObject and you got all your synchronization ducks in a row on your
DequeStream, or whatever you called it.
Now look what I've done. I've found a use in Java for the "curiously
recurring template pattern", which someone just said didn't work in
Java; I've found a real use for concurrency on single-core machines for
a task that doesn't involve network or disk I/O; and I've gone and
solved some other problems.
I thought I might even have found a need for
@SuppressWarnings("unchecked") that doesn't involve interfacing to
nongeneric legacy code, but I guess I haven't really, since clone isn't
generic and Object isn't generic. (Imagine if they'd had Object
parametrized by the run-time class of the actual object, like Foo is
above; Java might have been very different and not just in the clone
method.)
- jenny