Which is better option: Cloning or Copy constructors?

by Prasanth Gullapalli

Here is how I started writing this article. I have read this statement so many times: “Cloning becomes difficult when the object has references to mutable final fields.” And every time I google about it, understand what exactly this means and as part of the process forget about it too. So thought I would blog this so that this will serve as my immediate reference.

Cloning an object, what I could recall from my OOP course in my graduate studies, is creating a similar copy of an object which basically should conform to the following rules:

(1) x.clone() != x
(2) x.clone().getClass() == x.getClass()
(3) x.clone().equals(x)

Note that condition (1) must always be satisfied in all the cases. Though conditions (2) and (3) are not absolute requirements, it is good to design clone method in such a way that these hold good. Before going ahead with discussion, here is the method signature of clone method in Object class:

protected native Object clone() throws CloneNotSupportedException;

So as you notice the protected modifier, it is not possible for us to call clone() method directly on any object. We have to override this method as a public method and provide implementation for it in our class in order to access it. If no specific implementation is needed, we can just return super.clone(). As covariant returns are possible after Java 5, we can modify the return value of clone to return the object of our class. So if we are writing our employee class, here is how the clone() method would like:

@Override
public Employee clone() throws CloneNotSupportedException {
	return (Employee) super.clone();
}

But note that the clone method in Object class checks if our class implements Cloneable interface. If it does not implement it, then it throws CloneNotSupportedException. Otherwise it creates a new copy. But note that clone method never calls constructor to create copy of an object. 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. The clone method instead does field-by-field copy of instance properties from the object memory and returns it to caller. So it is must for a class to implement the marker interface, Cloneable, if it has to provide an option for cloning it without getting CloneNotSupportedException. But notice that the code which calls clone() should handle this exception. Otherwise it would result in compiler error. Yes, it’s a pain point and is criticized for this.

Let’s take an example now: Case (1):

public class Employee implements Cloneable{
	private String name;
	private String identifier;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getIdentifier() {
		return identifier;
	}
	public void setIdentifier(String identifier) {
		this.identifier = identifier;
	}
	
	@Override
	public Employee clone() throws CloneNotSupportedException {
		return (Employee)super.clone();
	}
	
	public void print() {
		System.out.println(Objects.toStringHelper(this).add("name:", name).add("id:", identifier).toString());
	}
	
	public static void main(String[] args) throws CloneNotSupportedException {
		Employee employee1 = new Employee();
		employee1.setName("Ram");
		employee1.setIdentifier("1");
		System.out.println("1: "+employee1);
		employee1.print();
		
		Employee employee2 = employee1.clone();
		System.out.println("2: "+employee2);
		employee2.print();
	}
}

Here is the output of this:

1: com.pramati.test.Employee@19821f
Employee{name:=Ram, id:=1}
2: com.pramati.test.Employee@de6ced
Employee{name:=Ram, id:=1}

As can be seen from the above example, clone() method has created a new Employee with values copied from the existing object. This is pretty straight forward and works fine as there are no object references in Employee class. Let us modify our class like this: Case (2):

public class PayPackDetails{
	private double basicSalary = 500000d;
	private double incentive = 50000d;
	
	public double getSalary() {
		return getBasicSalary()+getIncentive();
	}

	public double getBasicSalary() {
		return basicSalary;
	}
	
	public double getIncentive() {
		return incentive;
	}
	
	public void setBasicSalary(double basicSalary) {
		this.basicSalary = basicSalary;
	}
	
	public void setIncentive(double incentive) {
		this.incentive = incentive;
	}
}


public class Employee implements Cloneable {
	
	private String name;
	private String identifier;
	private PayPackDetails packDetails;
	
	public Employee(String name, String identifier, PayPackDetails packDetails) {
		this.name = name;
		this.identifier = identifier;
		this.packDetails = packDetails;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getIdentifier() {
		return identifier;
	}
	public void setIdentifier(String identifier) {
		this.identifier = identifier;
	}
	public PayPackDetails getPackDetails() {
		return packDetails;
	}
	
	@Override
	public Employee clone() throws CloneNotSupportedException {
		return (Employee)super.clone();
	}
	
	public void print() {
		System.out.println(Objects.toStringHelper(this).add("name:", name).add("id:", identifier).add("package:", packDetails.getSalary()).toString());
	}
	
	public static void main(String[] args) throws CloneNotSupportedException {
		Employee employee1 = new Employee("Ram","1",new PayPackDetails());
		System.out.println("1: "+employee1);
		employee1.print();
		
		Employee employee2 = employee1.clone();
		System.out.println("2: "+employee2);
		employee2.print();
	}
}

On running the main method we will get the following results:

1: com.pramati.clone.Employee@addbf1
Employee{name:=Ram, id:=1, package:=550000.0}
2: com.pramati.clone.Employee@de6ced
Employee{name:=Ram, id:=1, package:=550000.0}

This is fine. Now lets say, we modified our main method like this: Case (3):

public static void main(String[] args) throws CloneNotSupportedException {
	Employee employee1 = new Employee("Ram","1",new PayPackDetails());
	Employee employee2 = employee1.clone();
	employee2.setName("Krish"); employee2.setIdentifier("2");
	employee2.getPackDetails().setBasicSalary(700000d);
	employee1.print();
	employee2.print();
}

Now what do you think would the salary of employee1 be? As we have increased the salary of cloned employee, we naturally expect the salary to be increased for him. But the unexpected turn here is the salary for employee1 also gets increased. Here is the output or this:

Employee{name:=Ram, id:=1, package:=750000.0}
Employee{name:=Krish, id:=2, package:=750000.0}

Please note that when we clone an object, the constructor does not get called. It would rather make a field-by-field copy of all the member variables present in the address location of the original object. And now when there are object references, the reference gets copied but not the original object. Hence both the original and cloned objects point to the same member object. So changes made in one object would automatically be visible to the other. So how to resolve this problem?

The easiest solution is to implement clone method for PayPackDetails too and call it from Employee’s clone method. Case (4):

@Override
public Employee clone() throws CloneNotSupportedException {
	Employee employee = (Employee)super.clone();
	employee.packDetails = packDetails.clone();
	return employee;
}

Now on running main() method, it will give the right results as expected:

Employee{name:=Ram, id:=1, package:=550000.0}
Employee{name:=Krish, id:=2, package:=750000.0}

But if PayPackDetails is composed with other object references, we have to override clone method for that object too and call its clone method inside PayPackDetails. Also when ever we compose a new object in PayPackDetails, we have to modify the clone method too in PayPackDetails apart from implementing clone() method into the newly composed object. The composed object class should also implement Cloneable interface. As it is always the case, we also have to handle CloneNotSupportedException.

Now consider another case when PayPackDetails is declared final, this will make the situation even worse: Case (5):

public class Employee implements Cloneable {
	private String name;
	private String identifier;
	private final PayPackDetails packDetails;
	// -- Rest of the methods
}

As the field is declared final, we can not assign a new value to it in clone method as it is declared final. So how to deal with this? Here is the solution: Use a copy constructor and return the new instance from the clone.

public class Employee implements Cloneable {
	
	private String name;
	private String identifier;
	private final PayPackDetails packDetails;
	
	public Employee(String name, String identifier, PayPackDetails packDetails) {
		this.name = name;
		this.identifier = identifier;
		this.packDetails = packDetails;
	}
	
	protected Employee(Employee emp) throws CloneNotSupportedException{
		name = emp.name;
		identifier = emp.identifier;
		packDetails = emp.packDetails.clone();
	}
	
	@Override
	public Employee clone() throws CloneNotSupportedException {
		return new Employee(this);
	}
	
	public void print() {
		System.out.println(Objects.toStringHelper(this).add("name:", name).add("id:", identifier).add("package:", packDetails.getSalary()).toString());
	}
}

Note that the copy constructor access modifier is protected. So now the question comes: why can’t we use a copy constructor for PayPackDetails too instead of clone method? And the answer is: Yes, we can use it. Case (6):

public class PayPackDetails {

	private double basicSalary = 500000d;
	private double incentive = 50000d;
	
	public PayPackDetails(PayPackDetails details){
		basicSalary = details.getBasicSalary();
		incentive = details.getIncentive();
	}
	
	public static void main(String[] args) {
		Employee employee1 = new Employee("Ram","1",new PayPackDetails());
		employee1.print();
		Employee employee2 = new Employee(employee1);
		employee2.print();
	}
}
public class Employee {
	
	private String name;
	private String identifier;
	private final PayPackDetails packDetails;
	
	protected Employee(Employee emp) {
		name = emp.name;
		identifier = emp.identifier;
		packDetails = new PayPackDetails(emp.packDetails);
	}
	
    // .. Other methods
	
}

This is the best case so far and here is the output for this program:

Employee{name:=Ram, id:=1, package:=550000.0}
Employee{name:=Ram, id:=1, package:=550000.0}

In fact it is the best way to do it as it resolves many problems with flawed clone method:

1. None of the classes have to implement the marker interface Cloneable
2. As clone is not needed, there is no need of catching CloneNotSupportedException
3. As clone is not needed, there is no need of typecasting the object on calling super.clone()

But here comes the problem: Say suppose you have a subclass for PayPackDetails. Case (7):

public class AdvancedPayPackDetails extends PayPackDetails {
	private double flexiblePayPercent = 10d;

	public AdvancedPayPackDetails(AdvancedPayPackDetails details) {
		super(details);
		flexiblePayPercent = details.getFlexiblePayPercentage();
	}

	@Override
	public double getSalary() {
		return super.getSalary()+(getBasicSalary()*getFlexiblePayPercentage()/100);
	}
	
	public double getFlexiblePayPercentage() {
		return flexiblePayPercent;
	}
	
	public void setFlexiblePayPercent(double flexiblePayPercent) {
		this.flexiblePayPercent = flexiblePayPercent;
	}

    public static void main(String[] args) throws CloneNotSupportedException {
		Employee employee1 = new Employee("Ram","1",new AdvancedPayPackDetails());
		employee1.print();
		Employee employee2 = employee1.clone();
		employee2.print();
	}

}

And now on running the main method, it will give us the output:

Employee{name:=Ram, id:=1, package:=600000.0}
Employee{name:=Ram, id:=1, package:=550000.0}

And the reason is obvious. The copy constructor of Employee did not know about this new class(AdvancedPayPackDetails) created. We can actually modify the Employee constructor to include instanceOf checks for PayPackDetails, but this is not the right way of doing things. Rather it’s better if we revert back to our earlier solution where we used copy constructor in case of final fields and use clone method for the classes which have Inheritance hierarchy(Solution of case (5)).

Conclusion: As we have seen all the way through this article, implementing clone method in the right way is very complicated. So it is better to stay away from clones as much as possible. It is better to go with copy constructors as long as the composed objects does not have any inheritance hierarchy.

About these ads

One thought on “Which is better option: Cloning or Copy constructors?

  1. Both copy constructors and cloning have their own issues. Personally, I try to avoid them as much as possible and if I can’t I prefer to write my own method with clear semantics (e.g. getCopy() ).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s