Java Copy Constructor

1. Introduction

A copy constructor in a Java class is a constructor that creates an object using another object of the same Java class.

That’s helpful when we want to copy a complex object that has several fields, or when we want to make a deep copy of an existing object.

2. How to Create a Copy Constructor

To create a copy constructor, we can first declare a constructor that takes an object of the same type as a parameter:

public class Employee {
    private int id;
    private String name;
  
    public Employee(Employee employee) {
    }
}

Then, we copy each field of the input object into the new instance:

public class Employee {
    private int id;
    private String name;
    
    public Employee(Employee employee) {
        this.id = employee.id;
        this.name = employee.name;
    }
}

What we have here is a shallow copy, which is fine since all of our fields – an int and a String in this case – are either primitive types or immutable types.

If the Java class has mutable fields, then we can instead make a deep copy inside its copy constructor. With a deep copy, the newly created object is independent of the original one because we create a distinct copy of each mutable object:

public class Employee {
    private int id;
    private String name;
    private Date startDate;

    public Employee(Employee employee) {
        this.id = employee.id;
        this.name = employee.name;
        this.startDate = new Date(employee.startDate.getTime());
    }
}

3. Copy Constructor vs. Clone

In Java, we can also use the clone method to create an object from an existing object. However, the copy constructor has some advantages over the clone method:

  1. The copy constructor is much easier to implement. We do not need to implement the Cloneable interface and handle CloneNotSupportedException.
  2. The clone method returns a general Object reference. Therefore, we need to typecast it to the appropriate type.
  3. We can not assign a value to a final field in the clone method. However, we can do so in the copy constructor.

4. Inheritance Issues

Copy constructors in Java are not inheritable by subclasses. Therefore, if we try to initialize a child object from a parent class reference, we will face a casting issue when cloning it with the copy constructor.

To illustrate this issue, let’s first create a subclass of Employee and its copy constructor:

public class Manager extends Employee {
    private List<Employee> directReports;
    // ... other constructors

    public Manager(Manager manager) {
        super(manager.id, manager.name, manager.startDate);
        this.directReports = directReports.stream()
          .collect(Collectors.toList());
    }
}

Then, we declare an Employee variable and instantiate it with the Manager constructor:

Employee source = new Manager(1, "VietManager", startDate, directReports);

Since the reference type is Employee, we have to cast it to Manager type so that we can use the copy constructor of the Manager class:

Employee clone = new Manager((Manager) source);

We may get ClassCastException at runtime if the input object is not an instance of Manager class.

One way to avoid casting in the copy constructor is to create a new inheritable method for both classes:

public class Employee {
   public Employee copy() {
        return new Employee(this);
    }
}

public class Manager extends Employee {
    @Override
    public Employee copy() {
        return new Manager(this);
    }
}

In each class method, we call its copy constructor with the input of this object. In this way, we can guarantee that the generated object equals the caller object:

Employee clone = source.copy();

5. Conclusion

In this tutorial, we showed how to create a copy constructor with some code examples. Also, we discussed several reasons why we should avoid the clone method.

Copy constructor has a casting issue when we use it to clone a child class object whose reference type is the parent class. We provided one solution for this issue.

As always, the source code for the tutorial is available over on GitHub.