Learnitweb

Object cloning in Java

1. Introduction

While writing programs sometimes there is a need to create a copy of the object, or we can say need to clone an object. The process of creating the clone is defined by the object itself. In this tutorial, we’ll discuss two main aspects to cloning in Java:

  • Cloneable interface
  • clone() method of Object class

The intention of cloning an object x of type T is to create an new instance of the same type T and initialize its fields with the same content of object x. The cloned object and the original object x should be different objects and changing one object should not affect the other object.

2. Cloneable interface

The class which implements the Cloneable interface indicates that it is possible to create a clone of the objects of the class. Object.clone() method then can be used to create a copy of the object.

If the class does not implement Cloneable then trying to create a clone of the object by calling clone() method results in the exception CloneNotSupportedException being thrown.

Cloneable is a marker interface and does not contain any method. The main purpose of this interface is to indicate that the object is cloneable.

It is the convention that the class which implements Cloneable should override Object.clone() method.

Object class itself does not implement Cloneable, so the objects of Object class can not be cloned.

3. clone() method of Object

The signature of clone() method is:

protected Object clone() throws CloneNotSupportedException

As you can notice, the clone() method of Object is protected. The class which override this method should make it public.

java.lang.Object provides default implementation of clone() method in Java. It’s declared as native in the Object class, so implemented in native code. By convention, you should use super.clone() method in the overridden method so that it uses the Object.clone() method.

This method creates and returns a copy of this object. How should a clone of the object should look like depends on the class. Generally for any given object obj the cloning proves should meet the following conditions:

  • obj.clone() != obj should be true.
  • obj.clone().getClass() == obj.getClass() should be true
  • obj.clone().equals(obj) should be true

Note that these are not absolute conditions to meet. While cloning objects, the general intention is that the cloned object should not have a common mutable field, i.e. the change of a mutable field in cloned object should not affect the original object or vice-versa. If there is any such field, then changes may be required for that specific field(s). One such example is cloning an immutable object. In such case, copies of reference type field(s) is created.

4. Shallow copy and Deep copy

The clone() method creates a new instance of the class of the object to be cloned and initializes all its fields with the same content as of cloned object. The contents of the fields are not themselves cloned. So we can say that the clone() creates a “shallow” copy of the object not a “deep” copy. Let us understand this with the help of an example.

Suppose we have a class Employee.

public class Employee implements Cloneable {
	int id;
	String name;
	Address address;
}

When the object of this class is created by invoking clone() method, the contents of id, name and address will be copied to the cloned object as if by assignment. Since address is a reference variable, the cloned object will have the same address value and both objects will point to the same object in the memory. The reason is the content of the address field are not cloned. This is an example of “shallow” copy.

By default, clone() creates a shallow copy.

5. Advantages of cloning

  • It is a simple way to creating copy of objects.
  • clone() method is a convenient way of copying arrays.
  • Cloning is a reusable way of creating objects. It is possible for you to define your own logic while creating a clone based on requirement.

6. Disadvantages of cloning

  • Cloning an object in Java involves some code change like implementing Cloneable interface, overriding clone() method.
  • Cloning an object is different than using a constructor. So we have no control over object creation as we have while using constructor.

7. Example of shallow copy

Let us create a class Employee which implements Cloneable.

public class Employee implements Cloneable {
	int id;
	String name;
	Address address;

	// constructor
	public Employee(int id, String name, Address address) {
		super();
		this.id = id;
		this.name = name;
		this.address = address;
	}

	// override clone method
	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}

	// regular getter and setter
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", address=" + address + "]";
	}
}

The Address class has the following code:

class Address {
	String city;
	String state;

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getState() {
		return state;
	}

	public void setState(String state) {
		this.state = state;
	}

	public Address(String city, String state) {
		super();
		this.city = city;
		this.state = state;
	}

	@Override
	public String toString() {
		return "Address [city=" + city + ", state=" + state + "]";
	}
}

Lets now test the cloning of Employee objects.

public class Test {

	public static void main(final String[] arguments) throws CloneNotSupportedException {
		Object o;
		// create an Address
		Address address = new Address("dummy city", "dummy state");

		// create an Employee
		Employee e1 = new Employee(1, "James", address);

		// clone an Employee
		Employee e2 = (Employee) e1.clone();

		// change city in address in Employee object
		e1.getAddress().setCity("new dummy city");

		// print Employee e1
		System.out.println("Employee e1: " + e1);

		// changes made in e1 should also reflect in e2 as this is shallow copy
		System.out.println("Employee e2: " + e2);
	}
}

Output

Employee e1: Employee [id=1, name=James, address=Address [city=dummy city, state=dummy state]]
Employee e2: Employee [id=1, name=James, address=Address [city=dummy city, state=dummy state]]

You can notice that clone() creates a shallow copy as change in address of e1 changes the address in e2.

8. Example of deep copy

Let us change our previous example of shallow copy to make it deep copy. We’ll update the clone() method of Employee and Address class.

public class Employee implements Cloneable {
	int id;
	String name;
	Address address;

	// constructor
	public Employee(int id, String name, Address address) {
		super();
		this.id = id;
		this.name = name;
		this.address = address;
	}

	// override clone method
	public Object clone() throws CloneNotSupportedException {
		Employee employee = null;
		try {
			employee = (Employee) super.clone();
		} catch (CloneNotSupportedException e) {
			employee = new Employee(this.getId(), this.getName(), this.getAddress());
		}
		employee.address = (Address) this.address.clone();
		return employee;
	}

	// regular getter and setter
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", address=" + address + "]";
	}
}

class Address {
	String city;
	String state;

	public Address(String city, String state) {
		super();
		this.city = city;
		this.state = state;
	}

	@Override
	public Object clone() {
		try {
			return (Address) super.clone();
		} catch (CloneNotSupportedException e) {
			return new Address(this.getCity(), this.getState());
		}
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getState() {
		return state;
	}

	public void setState(String state) {
		this.state = state;
	}

	@Override
	public String toString() {
		return "Address [city=" + city + ", state=" + state + "]";
	}
}

Let us now test our code.

public class Test {

	public static void main(final String[] arguments) throws CloneNotSupportedException {
		Object o;
		// create an Address
		Address address = new Address("dummy city", "dummy state");

		// create an Employee
		Employee e1 = new Employee(1, "James", address);

		// clone an Employee
		Employee e2 = (Employee) e1.clone();

		// change city in address in Employee object
		e1.getAddress().setCity("new dummy city");

		// print Employee e1
		System.out.println("Employee e1: " + e1);

		// changes made in e1 should also reflect in e2 as this is shallow copy
		System.out.println("Employee e2: " + e2);
	}
}

Output

Employee e1: Employee [id=1, name=James, address=Address [city=new dummy city, state=dummy state]]
Employee e2: Employee [id=1, name=James, address=Address [city=dummy city, state=dummy state]]