Copy constructor

Hi All:

I have a question about semantics of how the copy constructor should be implemented (I know that it is not part of the language)

In case of NULL input parameter, which of the following is the best practice and why?

1) Ignore the input parameter and construct the object with default values

public class Foo

{

public Foo(Foo foo)

{

if ( null != foo )

i = foo.i;

}

private int i=0;

}

2) Throw an exception if the input parameter is null

public class Foo

{

public Foo(Foo foo)

{

if ( null == foo )

throw new Exception("Parameter cannot be null")

i = foo.i;

}

private int i=0;

}

3) Do not do anything, so at runtime NullPointerException gets thrown. (JDK seems to be doing this)

public class Foo

{

public Foo(Foo foo)

{

i = foo.i; // If foo is null, it will throw NullPointerException

}

private int i=0;

}

Appreciate your comments.

[1061 byte] By [Vgajula] at [2007-9-26 2:34:20]
# 1

There's no answer to your question, it depends on the context. But when treating null values as invalid, make sure you avoid paradoxes :)// this paradox existed before computer sciences :)

// It justs demonstrate that the question goes beyond

// java, enjoy :)

// an egg can only be created by a given chicken

final class Egg {

Egg( Chicken chicken ) {

if ( chicken == null )

throw new NullPointerException();

}

}

// a chicken can only comes from an egg

final class Chicken {

Chicken( Egg egg ) {

if ( egg == null )

throw new NullPointerException();

}

}

remu at 2007-6-29 9:58:49 > top of Java-index,Core,Core APIs...
# 2

Don't use copy constructors! Use clone()!

The reason is that clone() works polymorphically, but copy constructors do not.

For example, what if you had a Foo reference, and wanted to copy the object:

Foo foo = getFoo();

Foo fooCopy = new Foo(foo);

This works only if getFoo() returns a reference to a Foo object. If someone else creates SubFoo class, and changes getFoo() to return a reference to a SubFoo rather than a Foo, everything of course is all FUBAR because your Foo copy constructor cannot copy a SubFoo!

The solution is to use clone():

Foo foo = getFoo();

Foo fooCopy = foo.clone()

Now if someone creates a SubFoo class, all they need to do is provide the correct implementation of clone(), and the SubFoo will be copied properly.

schapel at 2007-6-29 9:58:49 > top of Java-index,Core,Core APIs...
# 3

You have 3 different ways to use a clone-like feature:

1) implements the Cloneable interface, and do nothing else.

2) implement the Cloneable interface, use super.clone() in it, and copy some fields yourself, do that to have a more or less deep copy

3) do not implement the Cloneable interface, but then do not use super.clone() in your clone() code.

here are some sample code:

// case 1), implement tha interface, do nothing else

Class A implements Cloneable {

private int a;

A( int a ) {

this.a = a;

}

}

// case 2) implement the interface, but do a deeper copy than the default one

Class B implements Cloneable {

private List l1, l2;

// the returned clone share the same l1 instance,

// but l2 is different.

public Object clone() {

B clone = (B) super.clone();

clone.l2 = new ArrayList();

clone.l2.addAll( this.l2 );

return clone;

}

}

// case 3), nothing to do with the Cloneable interface

class C {

public C myClone() {

// here the constructor by copy can be usefull.

return new C( this );

}

}

The major problem with clone, is that you need a instance to clone it. With a copy constructor that do accept null parameter, you can still create a new object. But yes, I would rather go for clone, using the interface.

remu at 2007-6-29 9:58:49 > top of Java-index,Core,Core APIs...
# 4

Moreover, you can combine clone and copy constructor:

class A {

protected A(A copy) {

...

}

public Object clone() {

return new A(this);// no more null creep

}

}

...

// somewere in the code

A a = new A();

A b = (A)a.clone();

Use a copy-like constructor that handles nulls to implement tree-like structures:

class B {

B(B parent) {

// parent == null if this is a root element

}

}

vograh at 2007-6-29 9:58:49 > top of Java-index,Core,Core APIs...
# 5

No! That is not how to write clone()!

First, the class should implement Cloneable, and second, clone() should always call super.clone() to create an object of the correct class:

class A implements Clone {

public Object clone() {

try {

return super.clone();

} catch (CloneNotSupportedException e) {

throw new InternalError();

}

}

}

Now, if class SubA extends A, the inherited clone method creates a SubA object rather than an A object.

schapel at 2007-6-29 9:58:49 > top of Java-index,Core,Core APIs...
# 6

if you do not do any extra job in the clone() implementation, you don't even have to implement it, since it's already coded into Object. The Cloneable interface is just a tagging interface so that:

1) the method becomes public

2) the default Object.clone() implementation do not throw the CloneNotSupportedException.

So that leaves us the following code:class A implements Cloneable {

// no need to write the clone() method

}

And that only one way to do it.

remu at 2007-6-29 9:58:49 > top of Java-index,Core,Core APIs...
# 7

If you want to accept null, use a factory method

public class Foo implements Cloneable {

public static Foo copy(Foo f) {

if (f == null)

return null;

return f.clone();

}

protected Object clone() {

try {

return super.clone();

} catch(CloneNotSupportedException e) {

throw new InternalError(e.toString());

}

}

}

nerd2004 at 2007-6-29 9:58:49 > top of Java-index,Core,Core APIs...
# 8

Object.clone() is declared as

protected Object clone() throws CloneNotSupportedException

Therefore, it is necessary to override clone() because:

(1) It has to be public instead of protected.

and

(2) It's a hell of a lot easier to put catch(CloneNotSupportedException e) in clone() than it is to write it in every block of code that calls clone().

nerd2004 at 2007-6-29 9:58:49 > top of Java-index,Core,Core APIs...
# 9

ahh yes, you're right, we still have to rewrite a pseudo function that just do the super.clone() call. I though Cloneable had a public Object clone() method but I was wrong. And it has no such method because if it has, classes that implement the interface would not compile at all.

my mistake.

remu at 2007-6-29 9:58:49 > top of Java-index,Core,Core APIs...
# 10
The combination of the clone and the factory method looks like a winner. All the copy constructors I've seen have serious flaws.
schapel at 2007-6-29 9:58:49 > top of Java-index,Core,Core APIs...