Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy an object in Java

Tags:

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 overriden with a public method in my class. But I cannot find an explanation on how to do this. How could this be done?

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

like image 807
Tray Avatar asked Jan 24 '09 10:01

Tray


2 Answers

Another option by using Copy Constructor (from Java Practices):

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());     } }  
like image 131
ecleel Avatar answered Oct 06 '22 19:10

ecleel


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.

like image 37
Ronald Blaschke Avatar answered Oct 06 '22 17:10

Ronald Blaschke