Core Java

Which is better option: Cloning or Copy constructors?

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.
 

Reference: Which is better option: Cloning or Copy constructors? from our JCG partner Prasanth Gullapalli at the prasanthnath blog.

Prasanth Gullapalli

Prasanth is passionated about technology and specializes in application development in distributed environments. He has always been fascinated by the new technologies and emerging trends in software development.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Jake Zimmerman
Jake Zimmerman
10 years ago

This is why I really enjoy immutable objects; there is no point in cloning or copying an immutable object because anything that allows you to make a change (any of the techniques) to an object automatically creates a new object.
Obviously, though, there are times where is it difficult, maybe impossible (I’m not convinced ‘impossible’ is ever the case, though), to make something be immutable. At those times, your advice here is helpful.

Back to top button