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.
- 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.
- The clone() method returns an object reference, and explicit casting is required.
- 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.