Signup/Sign In
PUBLISHED ON: AUGUST 18, 2021

Copy Constructor in Java

Copy constructors create a new object by using the values of an existing object. Copy constructors are an alternative to other cloning approaches like the Cloneable interface. These constructors can create shallow as well as deep clones.

In this tutorial, we will learn how to create copy constructors in Java.

Copy Constructor for a Simple Class

Copy constructor, just like any other constructor, should have the same name as the class. A copy constructor takes an object of the class as a parameter. It initializes the fields by using the values of the input object. Let's create a copy constructor for a simple Student class that contains a String name field and a double GPA field.

class Student
{
	private String name;
	private double gpa;
	
	//parameterized constructor
	Student(String name, double gpa)
	{
		this.name = name;
		this.gpa = gpa;
	}	
	//Copy Constructor
	Student(Student s)
	{
		this.name = s.getName();
		this.gpa = s.getGpa();
	}	
	//getters and setters
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getGpa() {
		return gpa;
	}
	public void setGpa(double gpa) {
		this.gpa = gpa;
	}
}
public class Demo
{	
	public static void main(String[] args)
	{
		Student s1 = new Student("Justin", 8.5);
		Student cloneOfS1 = new Student(s1);
		
		s1.setName("Jessica");//Changing the original object
		
		System.out.println("Student-1: " + s1.getName());
		System.out.print("Student-2: " +  cloneOfS1.getName());
	}
}


Student-1: Jessica
Student-2: Justin

As we can see, the copy constructor creates a clone of the s1 Student object. Changing the values of s1 doesn't affect the cloned object.

Copy Constructor for Classes with Referenced Types

In the previous section, the copy constructor created a shallow clone. But since the Student class only contains primitives or immutable fields, the clone is not affected by changes in the original object.

If the class fields are primitives or immutables, then shallow cloning is the same as deep cloning. Let's add an Address class field to our Student class and create a shallow copy from our constructor.

class Student
{
	private String name;
	private double gpa;
	private Address studentAddress;
	
	//parameterized constructor
	Student(String name, double gpa, Address add)
	{
		this.name = name;
		this.gpa = gpa;
		this.studentAddress = add;
	}	
	//Copy Constructor
	Student(Student s)
	{
		this.name = s.getName();
		this.gpa = s.getGpa();
		this.studentAddress = s.getStudentAddress();
	}	
    //getters and setters
	public Address getStudentAddress() {
		return studentAddress;
	}

	public void setStudentAddress(Address studentAddress) {
		this.studentAddress = studentAddress;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getGpa() {
		return gpa;
	}
	public void setGpa(double gpa) {
		this.gpa = gpa;
	}
}
class Address
{
	int postalCode;
	Address(int s)
	{
		this.postalCode = s;
	}
}
public class Demo
{	
	public static void main(String[] args)
	{
		Address add = new Address(10012);
		Student s1 = new Student("Justin", 8.5, add);
		Student cloneOfS1 = new Student(s1);
		
		add.postalCode = 20012;//Altering the Address will affect the cloned object
		
		System.out.println("Student-1: " + s1.getStudentAddress().postalCode);
		System.out.print("Student-2: " +  cloneOfS1.getStudentAddress().postalCode);
	}
}


Student-1: 20012
Student-2: 20012

To create a deep copy, we need to create a new object in our constructor. Changing the references will not work. The modified copy constructor for the Student class is shown below.

//Copy Constructor
Student(Student s)
{
	this.name = s.getName();
	this.gpa = s.getGpa();
    //creating a new address object
	this.studentAddress = new Address(s.getStudentAddress().postalCode);
}

Let's use the above constructor and view the result.

public class Demo
{	
	public static void main(String[] args)
	{
		Address add = new Address(10012);
		Student s1 = new Student("Justin", 8.5, add);
		Student cloneOfS1 = new Student(s1);
		
		add.postalCode = 20012;
		
		System.out.println("Student-1: " + s1.getStudentAddress().postalCode);
		System.out.print("Student-2: " +  cloneOfS1 .getStudentAddress().postalCode);
	}
}


Student-1: 20012
Student-2: 10012

As shown above, altering the original object does not affect the clone. This indicates that a deep copy was created.

Copy Constructors vs Clone() Method

As discussed in the previous sections, overriding the clone() method of the Cloneable interface can also create a deep copy. However, there are some advantages of using the copy constructor for creating a deep copy.

  1. It is a lot simpler to create and understand a copy constructor. With the Cloneable interface, we need to override the clone() method and handle the CloneNotSupportedException.
  2. The clone() method returns an object reference, and explicit casting is required.
  3. We cannot assign the value to a final field using the clone method. But it can be done using copy constructors.

Inheritance Problem with Copy Constructors

A drawback of copy constructors is that they don't get inherited by child classes. If a child class type is referenced using parent class, then we cannot use the copy constructor of the child class. The following code demonstrates this.

class ClassA
{
	int x;
	ClassA(int a)
	{
		this.x = a;
	}
	//copy constructor of Parent class
	ClassA(ClassA object)
	{
		this.x = object.x;
	}
}

class ClassB extends ClassA
{
	int y;
	ClassB(int a, int b)
	{
		super(a);
		this.y = b;
	}
	//copy constructor of child class
	ClassB(ClassB object)
	{
		super(object.x);
		this.y = object.y;
	}
}
public class Demo
{	
	public static void main(String[] args)
	{
		ClassA b = new ClassB(100, 200);//Assigning child type to parent reference
		ClassB cloneB = new ClassB(b);//Compilation Error
	}
}


Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The constructor ClassB(ClassA) is undefined

at Demo.main(Demo.java:37)

We can solve this issue by using an explicit cast. But we may get a ClassCastException if the object is not of the child class type.

public class Demo
{	
	public static void main(String[] args)
	{
		ClassA b = new ClassB(100, 200);
		ClassB cloneB = new ClassB( (ClassB) b);//Using a cast
	}
}

We can also use an additional method in both classes to create a copy. These methods will simply call the copy constructor of their classes.

class ClassA
{
	int x;
	ClassA(int a)
	{
		this.x = a;
	}
	//copy constructor
	ClassA(ClassA object)
	{
		this.x = object.x;
	}
	// copyObject() method that uses the copy constructor
	public ClassA copyObject()
	{
		return new ClassA(this);
	}
}

class ClassB extends ClassA
{
	int y;
	ClassB(int a, int b)
	{
		super(a);
		this.y = b;
	}
	//copy constructor
	ClassB(ClassB object)
	{
		super(object.x);
		this.y = object.y;
	}
	// copyObject() method that uses the copy constructor
	public ClassB copyObject()
	{
		return new ClassB(this);
	}
}

public class Demo
{	
	public static void main(String[] args)
	{
		ClassA b = new ClassB(100, 200);
		ClassA cloneB = b.copyObject();
	}
}

Summary

Copy constructors are a simple and elegant way of cloning objects. They can create shallow as well as deep copies. For shallow clones, we just need to set the references to the input object fields. But for a deep copy, we need to create and assign new objects.

A drawback of copy constructors is that they are inherited. However, we can overcome this drawback by adding a method that calls the copy constructor in the parent and child classes.



About the author:
I am a 3rd-year Computer Science Engineering student at Vellore Institute of Technology. I like to play around with new technologies and love to code.