[java] cloning objects correctly

Started by
5 comments, last by CaptainJester 18 years, 6 months ago
As I just learned, cloning in Java can't be achieved by using a "copy constructor". You have to implement the Interface Cloneable instead and call super.clone() in your own clone() method for a shallow copy. After that, you must take whatever steps necessary to make a deep copy (if that is the desired behavior). As I've never done this before I post the clone() methods of two of my classes and would ask you to check if I got this right: shallow copy of two int values:

public class Slice implements Cloneable
{
	private int _bot;
	private int _top;

	[...]

	public Object clone()
	{
		Slice that = null;
		try
		{
			that = (Slice) super.clone();
		}
		catch (CloneNotSupportedException e)
		{
			e.printStackTrace();
			System.exit(-1);
		}
		return that;
	}
}

array has to be copied to get a deep copy, rest is shallow:

public class Piece implements Cloneable
{
	private Slice[] _slice;
	private int _capacity;
	private int _left;
	private int _right;

	[...]

	public Object clone()  
	{
		assert isReduced() : "Vorbedingung verletzt: isReduced()";
		Piece that = null;
		try
		{
			that = (Piece) super.clone();
		}
		catch (CloneNotSupportedException e)
		{
			e.printStackTrace();
			System.exit(-1);
		}
		
		that._slice = new Slice[_capacity];
		
		for (int i=_left; i<_right; i++)
		{
			that._slice = (Slice) _slice.clone();
		}

		assert that.isReduced() : "Nachbedingung verletzt: result.isReduced()";
		return that;
	}
}

Is there any way to get rid of these nasty typecasts? I thought I would have gotten rid of them at last with the introduction of generics.
Advertisement
You call super.clone() to make it a deep copy. But you also have to copy your own data.

public class Slice implements Cloneable{	private int _bot;	private int _top;	[...]	public Object clone()	{		Slice that = null;		try		{			that = (Slice) super.clone();                        that._bot =_bot;                        that._top =_top;		}		catch (CloneNotSupportedException e)		{			e.printStackTrace();			System.exit(-1);		}		return that;	}}

public class Piece implements Cloneable{	private Slice[] _slice;	private int _capacity;	private int _left;	private int _right;	[...]	public Object clone()  	{		assert isReduced() : "Vorbedingung verletzt: isReduced()";		Piece that = null;		try		{			that = (Piece) super.clone();                        that._capacity = _capacity;                        that._left= _left;                        that._right= _right;		}		catch (CloneNotSupportedException e)		{			e.printStackTrace();			System.exit(-1);		}				that._slice = new Slice[_capacity];				for (int i=_left; i<_right; i++)		{			that._slice = (Slice) _slice.clone();		}		assert that.isReduced() : "Nachbedingung verletzt: result.isReduced()";		return that;	}}
"None of us learn in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future!" - Michael Abrash[JavaGaming.org][The Java Tutorial][Slick][LWJGL][LWJGL Tutorials for NeHe][LWJGL Wiki][jMonkey Engine]
Quote:Original post by CaptainJester
You call super.clone() to make it a deep copy. But you also have to copy your own data.

I did a little more research - and these statements are definately wrong!

Quote:http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#clone()
If a class contains only primitive fields or references to immutable objects, then it is usually the case that no fields in the object returned by super.clone need to be modified.

The method clone for class Object performs a specific cloning operation. First, if the class of this object does not implement the interface Cloneable, then a CloneNotSupportedException is thrown. Note that all arrays are considered to implement the interface Cloneable. Otherwise, this method creates a new instance of the class of this object and initializes all its fields with exactly the contents of the corresponding fields of this object, as if by assignment; the contents of the fields are not themselves cloned. Thus, this method performs a "shallow copy" of this object, not a "deep copy" operation.

Quote:Original post by Fred304
Quote:Original post by CaptainJester
You call super.clone() to make it a deep copy. But you also have to copy your own data.

I did a little more research - and these statements are definately wrong!

Quote:http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#clone()
If a class contains only primitive fields or references to immutable objects, then it is usually the case that no fields in the object returned by super.clone need to be modified.

The method clone for class Object performs a specific cloning operation. First, if the class of this object does not implement the interface Cloneable, then a CloneNotSupportedException is thrown. Note that all arrays are considered to implement the interface Cloneable. Otherwise, this method creates a new instance of the class of this object and initializes all its fields with exactly the contents of the corresponding fields of this object, as if by assignment; the contents of the fields are not themselves cloned. Thus, this method performs a "shallow copy" of this object, not a "deep copy" operation.


what this means is that a clone of this class;
class Something implements Cloneable{public int primitiveI;public Integer classI;//implement the clone interface}

will create two classes, each with a copy of primitiveI with equivalent, and each with a copy of classI with equivalent values. It is at this point important to note that object references are more analogous to C++ pointers. classI is storing a memory address. So, that memory address is being copied and stored in the clone's classI member, but it points to the same instance of the Integer class that the original instance's classI member points to.

so, using the code from above
Something a = new Something();a.primitiveInt = 3;a.classInt = new Integer(7);Something b = a.clone();a.primitiveInt = 4;a.classInt.setValue(10); //if we could do such a thing, as opposed to creating a new Integer instance


a.primitiveInt is now 4, b.primitiveInt is still 3.
a.classInt and b.classInt are both 10.

[Formerly "capn_midnight". See some of my projects. Find me on twitter tumblr G+ Github.]

Quote:Original post by capn_midnight
Quote:Original post by Fred304
Quote:Original post by CaptainJester
You call super.clone() to make it a deep copy. But you also have to copy your own data.

I did a little more research - and these statements are definately wrong!


Maybe my post was ambiguous, I meant CaptainJester's statements are wrong.

Quote:
what this means is that a clone of this class [...] will create two classes

You don't clone the class, you clone the instance and get another instance.

Quote:
a.classInt.setValue(10); // if we could do such a thing

And since we can't it's not a problem that both instances refer to the same object: "If a class contains only primitive fields or references to immutable objects, then it is usually the case that no fields in the object returned by super.clone need to be modified."
Original post by Fred304
Quote:Original post by capn_midnight
Quote:
what this means is that a clone of this class [...] will create two classes

You don't clone the class, you clone the instance and get another instance.

typo
Quote:
Quote:
a.classInt.setValue(10); // if we could do such a thing

And since we can't it's not a problem that both instances refer to the same object: "If a class contains only primitive fields or references to immutable objects, then it is usually the case that no fields in the object returned by super.clone need to be modified."
but it is a problem for other classes that do allow you to edit their contents. I used Integer becuase it's familar, and it compares to the primitive type int. I used this example to explain *why* cloning doesn't always work, because you are copying memory addresses, not the actual instances.

[Formerly "capn_midnight". See some of my projects. Find me on twitter tumblr G+ Github.]

Sorry. Fred is right. It's been so long since I used cloning that I mixed it up.
"None of us learn in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future!" - Michael Abrash[JavaGaming.org][The Java Tutorial][Slick][LWJGL][LWJGL Tutorials for NeHe][LWJGL Wiki][jMonkey Engine]

This topic is closed to new replies.

Advertisement