Shallow vs Deep Copy of Object by Clone() method


Shallow copy of java objects

Previous post, we discussed on intention to create copy of object in java. In this post,we would discuss on different implementation of object copying. Before discuss on implementation, let us discuss on basic rule of clone copy implementation.

1) Copy of object and actual object should not refer at same object. If it is same, that is not copy of object and that would be two references to a same object. So it should satisfy the rule, Copy of object != object.
2) Class of copy object and actual object should be same. The rule is, Class of Copy of Object == Class of Object.
3) Copy of object and actual object should be logically. The rule is, Copy of object.equals(object).

The copy of object implementation should satisfy all the three rules. Now let us discuss on implementation. Implementation of copy can be done by,

  • Clone
  • Copy Constructor
  • Static Factory.

Creating copy by clone :

Java standard provides method in Object class – clone().  The logic of copying object should be written in clone() method. To implement clone, class should implement the interface Cloneable.

protected native Object clone() throws CloneNotSupportedException;

package info.javaarch;

class Employee implements Cloneable

private String name;           //  Only with primitive data types and String.Does not have reference type variables.
private String identifier;

public Employee(String name, String identifier) {

this.name = name;
this.identifier = identifier;

}

@Override
public Employee clone() throws CloneNotSupportedException {

return (Employee)super.clone();    //field-by-field copy of all the member variables

}

@Override
public boolean equals(Object e) {

if(e instanceof Employee) {

Employee e1 = (Employee)e;
return e1.name.equals(this.name);

}

return false;

}

}

public class CopyOfObjects {

public static void main(String[] args) throws CloneNotSupportedException {

Employee employee1 = new Employee(“John”, “0001”);
Employee employee2 = employee1.clone();

System.out.println(employee1 == employee2);         // Return false ; Satisfies Rule1
System.out.println(employee1.getClass().equals(employee2.getClass()));  // Returns true ; Satisfies Rule 2
System.out.println(employee1.equals(employee2));   // Returns true ; Satisfies Rule 3.

}

}

The above implementation works properly and satisfies all the implementation rules. Scenario to observe in above implementation,

  • Employee class has all the primitive data types (Java handles String object differently and in general to be treated as primitive) and does not have any reference type member variables.
  • “super.clone()” provides copy of actual object not only parent class variables. super.clone() is native method. It implemented by copying all the value of actual object to copy object. by field-by-field . Do not confuse by “super” keyword and assumes that only copy the super class variables.
  • Class does not have any references variables, so super.clone works as expected and satisfies all the rules.

Class with Reference type member variables:

Now let us add “Address” class and add Address as member in Employee and the implementation changes as below,

package info.javaarch;

class Employee implements Cloneable{

private String name;
private String identifier;
private Address address;

// Assume all the getter, setter and constructor here.

@Override
public Employee clone() throws CloneNotSupportedException {

return (Employee)super.clone();

}

// equals methods
}

class Address {

String street;
String city;

public Address(String s, String c) {

street = s;
city = c;

}
@Override
public boolean equals(Object e) {

if(e instanceof Address) {

Address e1 = (Address)e;
return e1.city.equals(this.city);

}

return false;

}

}

public class CopyOfObjects {

public static void main(String[] args) throws CloneNotSupportedException {

Address address = new Address(“Street No1”, “NJ”);
Employee employee1 = new Employee(“John”, “0001”,address);
Employee employee2 = employee1.clone();

System.out.println(employee1 == employee2); // Return false ; Satisfies Rule1
System.out.println(employee1.getClass().equals(employee2.getClass())); // Returns true ; Satisfies Rule 2
System.out.println(employee1.equals(employee2)); // Returns true ; Satisfies Rule 3.

System.out.println(employee1.getAddress() == employee2.getAddress()); // Return true ; Not Satisfies Rule1
System.out.println(employee1.getAddress().getClass().equals(employee2.getAddress().getClass())); // Returns true ; Satisfies Rule 2
System.out.println(employee1.getAddress().equals(employee2.getAddress())); // Returns true ; Satisfies Rule 3.

}

}

Now the rule 1 not satisfied for reference type member address.  Address object in copy of object and actual object refers the same object in the memory. Scenario to observer here is,

  • Class has reference type members
  • Clone method implemented as “super.clone”.
  • Copy implementation of Top class satisfies all 3 rules.
  • But copy of reference not satisfies the rule 1. It refers the same object.
  • This implementation creates copy of object and it referred as Shallow copy of object.

Proper copy of object should satisfy all 3 rules even for its member variables. So change the implementation by implementing clone in Address class and copy the address object by clone method.

package info.javaarch;

class Employee implements Cloneable{

private String name;
private String identifier;
private Address address;

// Assume getter,setter and constructor.

@Override
public Employee clone() throws CloneNotSupportedException {

Employee emp = (Employee)super.clone();
emp.address = this.address.clone(); // copy the member reference by super.clone()
return emp;

}

//Equals method.

}

class Address implements Cloneable {

String street;
String city;

// Assume getter,setter and constructor.

@Override
public Address clone() throws CloneNotSupportedException {    // Implements super.clone()

return (Address)super.clone();

}

}

public class CopyOfObjects {

public static void main(String[] args) throws CloneNotSupportedException {

Address address = new Address(“Street No1”, “NJ”);
Employee employee1 = new Employee(“John”, “0001”,address);
Employee employee2 = employee1.clone();

System.out.println(employee1 == employee2); // Return false ; Satisfies Rule1
System.out.println(employee1.getClass().equals(employee2.getClass())); // Returns true ; Satisfies Rule 2
System.out.println(employee1.equals(employee2)); // Returns true ; Satisfies Rule 3.

System.out.println(employee1.getAddress() == employee2.getAddress()); // Return true ; Not Satisfies Rule1
System.out.println(employee1.getAddress().getClass().equals(employee2.getAddress().getClass())); // Returns true ; Satisfies Rule 2
System.out.println(employee1.getAddress().equals(employee2.getAddress())); // Returns true ; Satisfies Rule 3.

}

}

Now all the rules satisfies by top class and it member reference object. Address object in copy of object and actual object refers the different object. Scenario to observer here is,

  • Class has reference type members
  • Clone method implemented as “super.clone” and it reference member classes implements clone().
  • Reference type members copied by clone() method in top class clone implementation.
  • Copy implementation of Top class and member object class satisfies all 3 rules.
  • This implementation creates proper copy of object and it referred as deep copy of object

In this post, we have implemented proper implementation of copy of object. Now clear about shallow vs deep copy objects.  But we can implement the copy of object in other two ways, copy constructor and static factory method implementation. Why clone() is not universal accepted solution for copy of object?

Issue with clone():

1). All the classes should implement cloneable interface and clone methods
2). final member can not be assigned in clone() method. Assigning final members is allowed in constructor.
3). clone() does not invoke any constructor. So if you want to keep track of number of instances getting created for a class by incrementing a static counter inside the constructor, this would not work as constructor never gets called.

Many java forum says that do not use clone() for copy of object, instead use copy constructors and static factory methods. There is no issue in using clone() if we write proper implementation and know the difference of shallow and deep copy. Moreover if your application does not have any dependency on above discussed issue of clone, you can go ahead with clone(). We would discuss copy constructor and static factory method implementation in next post.

You may also like