Anyrate, here is a unit test that highlights few notes I have gathered about Java clone. Look for the "Note" comments below.
package atest;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class CloneTest {
@Test
public void testFooClone() throws Exception {
Foo a = new Foo();
Foo b = a.clone();
assertThat(a != b, is(true));
assertThat(a.constructorCallsCount, is(1));
assertThat(a.name, is("foo"));
assertThat(b.constructorCallsCount, is(1)); // Note1: Wow, a new instance without calling constructor!
assertThat(b.name, is("foo"));
a.name = "fooX";
assertThat(a.name, is("fooX"));
assertThat(b.name, is("foo"));
Foo2 c = new Foo2();
c.ids.add("a");
Foo2 d = c.clone();
d.ids.add("b");
c.ids.clear();
c.ids.add("A");
assertThat(c != d, is(true));
assertThat(c.constructorCallsCount, is(2));
assertThat(c.name, is("foo"));
assertThat(d.constructorCallsCount, is(2));
assertThat(d.name, is("foo"));
assertThat(c.ids, hasItems("A"));
assertThat(c.ids.size(), is(1));
assertThat(d.ids, hasItems("a", "b"));
assertThat(d.ids.size(), is(2));
}
/** A simple clone examples with simple type fields. */
public static class Foo implements Cloneable {
static int constructorCallsCount = 0;
String name = "foo";
public Foo() {
constructorCallsCount++;
}
public Foo clone() {
// Note2: Catch checked exception here so client or subclass doesn't need to.
Foo result = null;
try {
result = (Foo)super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Unable to clone.", e);
}
return result;
}
}
/** A clone example that needs to fix field afterward super.clone(). */
public static class Foo2 extends Foo {
List<String> ids = new ArrayList();
public Foo2 clone() {
Foo2 result = (Foo2)super.clone(); // Note3: Some how, super clone will auto return the correct type!
result.ids = new ArrayList(ids); // Note4: need to fix non-simple field.
return result;
}
}
}