views:

2757

answers:

5

I have an object that I need to copy in Java. I need to create a copy and run some tests on it without changing the original object itself.

I assumed that I needed to use the clone() method, but this is protected. Having done some research on the net, I can see that this can be overrided with a public method in my class, but I cannot find an explanation of how to do this. How would this be done?

Also, is this the best way of achieving what I need?

+10  A: 

Some options:

  • You can implement Cloneable for your object and put clone() method as public. See full explanation here: http://www.cafeaulait.org/course/week4/46.html However, this produces a shallow copy and might be not something you want.
  • You can serialize and deserialize your object. You will need to implement Serializable interface for the object and all its fields.
  • You can use XStream to perform serialization via XML - you won't have to implement anything here.
Yoni Roit
+2  A: 

For test code Serialization is maybe the safest answer, especially if the object is already Serializable try Apache Commons SerializationUtils for an implementation.

Gareth Davis
+15  A: 

Another option by using Copy Constructor

public final class Galaxy {

public Galaxy (double aMass, String aName) {
 fMass = aMass;
 fName = aName;
}

/**
* Copy constructor.
*/
public Galaxy(Galaxy aGalaxy) {
this(aGalaxy.getMass(), aGalaxy.getName());
//no defensive copies are created here, since 
//there are no mutable object fields (String is immutable)
}

/**
* Alternative style for a copy constructor, using a static newInstance
* method.
*/
public static Galaxy newInstance(Galaxy aGalaxy) {
  return new Galaxy(aGalaxy.getMass(), aGalaxy.getName());
}

public double getMass() {
  return fMass;
}

/**
* This is the only method which changes the state of a Galaxy
* object. If this method were removed, then a copy constructor
* would not be provided either, since immutable objects do not
* need a copy constructor.
*/
public void setMass( double aMass ){
  fMass = aMass;
}

public String getName() {
return fName;
}

// PRIVATE /////
private double fMass;
private final String fName;

/**
* Test harness.
*/
public static void main (String... aArguments){
Galaxy m101 = new Galaxy(15.0, "M101");

Galaxy m101CopyOne = new Galaxy(m101);
m101CopyOne.setMass(25.0);
System.out.println("M101 mass: " + m101.getMass());
System.out.println("M101Copy mass: " + m101CopyOne.getMass());

Galaxy m101CopyTwo = Galaxy.newInstance(m101);
m101CopyTwo.setMass(35.0);
System.out.println("M101 mass: " + m101.getMass());
System.out.println("M101CopyTwo mass: " + m101CopyTwo.getMass());
}
}

from: http://www.javapractices.com/topic/TopicAction.do?Id=12

ecleel
I wouldn't use getter in the copy constructor. First you don't need them because you have always access to instance variables. Second if you make this yourself a practice you might lower encapsulation by creating too much getters. And finally it can be error prone. Imagine you tweak the value in getMass() than you would copy the tweaked value into the instance variable of the new object which would tweak the value a second time if you call getMass on the new object.
Norbert Hartl
A: 

Joshua Bloch has some interesting things to say about cloneable. Depending on the size/construction of the object, I'd add a copy constructor to the object, or serialise/deserialise using one of the solutions mentioned above.

Brian Agnew
+5  A: 

There are two popular approaches. One is to provide a clone method as you mentioned, like so.

public class C implements Cloneable {
    @Override public C clone() {
        try {
            final C result = (C) super.clone();
            // copy fields that need to be copied here!
            return result;
        } catch (final CloneNotSupportedException ex) {
            throw new AssertionError();
        }
}

Pay attention to the "copy fields ... here!" part. The initial result is only a shallow copy, meaning that if there's a reference to an object, both the original and result will share the same object. For example, if C contains private int[] data you'd probably want to copy that.

...
final C result = (C) super.clone();
result.data = data.clone();
return result;
...

Note that you don't need to copy primitive fields, as their content is already copied, or immutable objects, as they can't change anyways.

The second approach is to provide a copy constructor.

public class C {
    public C(final C c) {
        // initialize this with c
    }
}

Or a copy factory.

public class C {
    public static C newInstance(final C c) {
        return new C(c);
    }

    private C(final C c) {
        // initialize this with c
    }
}

Both approaches have their respective properties. clone is nice because its a method, so you don't have to know the exact type. In the end, you should always end up with a "perfect" copy. The copy constructor is nice because the caller has a chance to decide, as can be seen by the Java Collections.

final List c = ... 
// Got c from somewhere else, could be anything.
// Maybe too slow for what we're trying to do?

final List myC = new ArrayList(c);
// myC is an ArrayList, with known properties

I recommend choosing either approach, whichever suits you better.

I'd use the other approaches, like reflective copying or immediate serializing/deserializing, in unit tests only. To me, they feel less appropriate for production code, mainly because of performance concerns.

Ronald Blaschke