Core Java

Copy Constructor Java Example

In this tutorial, we shall discuss the Copy Constructor in Java. In the world of object-oriented programming, a copy constructor is a special type of constructor that takes an object of the same class as an argument and returns a duplicate or a copied instance of the input object initialized with the values of the member variables of the passed object.

1. Introduction

A constructor in a Java class is a block of code that on invocation creates a new instance(object) of the class and initializes its state. It looks like a method in its definition but has the name as that of the class and does not have any return type. Knowledge of constructors is a prerequisite for this tutorial.

Note: Copy constructors in Java differ from those in other programming languages like C++ in a way that Java runtime does not provide a default copy constructor if you don’t define your own.

java copy constructor

2. When to use it?

It is advisable to define a copy constructor in one of the following cases:

  • Copying a complex object which has several members.
  • Making deep copies of heavy objects.
  • An alternative and preferable approach for object cloning instead of the clone() method.

3. How to define it in Java

Let’s see a very simple example of a copy constructor in a class Car with two instance variables make and model. As explained earlier let’s create a constructor that takes an object of the same type Car.

Car.java

package org.adee.samples;

public class Car {

	private String make;
	private String model;

	public Car(String make, String model) {
		this.make = make;
		this.model = model;
	}

    // copy constructor
	public Car(Car car) {
		
	}
	
	// getters
}

Then, we initialize the new object created by this constructor by copying/assigning values of each member variable of the input object.

Car.java

package org.adee.samples;

public class Car {

	private String make;
	private String model;

	public Car(String make, String model) {
		this.make = make;
		this.model = model;
	}

	// copy constructor
	public Car(Car car) {
		this.make = car.getMake();
		this.model = car.getModel();
	}

	public String getMake() {
		return make;
	}

	public String getModel() {
		return model;
	}
}

In the above code, references to immutable types (String.class) are copied to the new object. The copied object points to the same memory location as that of the original but since the referred type is immutable, it never changes. What if the member variables of the class are mutable objects?

3.1 Shallow copy

Let’s take a look at the shallow mechanism using the copy constructor. In shallow copy, the references/handles of the input object are copied over to the newly created object.

Let’s demonstrate this with an example. We have defined classes Engine, CarWithEngine, and defined a copy constructor that does a shallow of the incoming object.

Engine.java

package org.adee.samples.shallowCopy;

public class Engine {

	private String engineModel;

	public Engine(String engineModel) {
		this.engineModel = engineModel;
	}

	public String getEngineModel() {
		return engineModel;
	}

	public void setEngineModel(String engineModel) {
		this.engineModel = engineModel;
	}
}

CarWithEngine.java

package org.adee.samples.shallowCopy;

public class CarWithEngine {

	private String make;
	private String model;
	private Engine engine;

	public CarWithEngine(String make, String model, Engine engine) {
		this.make = make;
		this.model = model;
		this.engine = engine;
	}

	// copy constructor
	public CarWithEngine(CarWithEngine carWithEngine) {
		this.make = carWithEngine.getMake();
		this.model = carWithEngine.getModel();

       // shallow copy
		this.engine = carWithEngine.getEngine();
	}

	public String getMake() {
		return make;
	}

	public String getModel() {
		return model;
	}

	public Engine getEngine() {
		return engine;
	}
}

The copy constructor in the above code does not do an in-depth copy of the input object’s field engine but merely copies its reference. Since they both point to the same memory location and the object Engine is mutable, modifying the copied object will also modify the source object. The below code illustrates this.

ShallowCopyDemo.java

package org.adee.samples.shallowCopy;

public class ShallowCopyDemo {

	public static void main(String[] args) {
		// original object
		CarWithEngine original = new CarWithEngine("Honda", "Brio",
                                        new Engine("ford-engine"));

		// copied object created with copy constructor
		CarWithEngine copied = new CarWithEngine(original);
		
		// modify the engine of the copied object
		copied.getEngine().setEngineModel("fakeEngine");
		
		// the engine of the source object is also modified
		// prints fakeEngine
		System.out.println(original.getEngine().getEngineModel());
	}
}

3.2 Deep copy

Copy constructors are useful for creating deep copies of objects. Deep copies solve the above problem by removing the dependencies of the copied objects on the source objects. In other words, a deep copy of an object will

  • be an exact copy of all the fields of the original object
  • have the exact copy of all the fields of any referred objects in the original object
  • be totally independent of the original object.

Modifying the cloned object will not be reflected in the original object and vice-versa.

CarWithEngine.java

package org.adee.samples.deepCopy;

import org.adee.samples.shallowCopy.Engine;

public class CarWithEngine {

	private String make;
	private String model;
	private Engine engine;

	public CarWithEngine(String make, String model, Engine engine) {
		this.make = make;
		this.model = model;
		this.engine = engine;
	}

	// copy constructor deep copy
	public CarWithEngine(CarWithEngine carWithEngine) {
		this.make = carWithEngine.getMake();
		this.model = carWithEngine.getModel();
		// this statement calls the copy constructor of class Engine
		this.engine = new Engine(carWithEngine.getEngine());
	}

	public String getMake() {
		return make;
	}

	public String getModel() {
		return model;
	}

	public Engine getEngine() {
		return engine;
	}
}

In the code above, we create a new instance of the class Engine by invoking its copy constructor from within the copy constructor of CarWithEngine. This instance is then assigned back to the copied instance of the CarWithEngine class and solves the reference problem of Shallow copy.

DeepCopyDemo.java

package org.adee.samples.deepCopy;

import org.adee.samples.deepCopy.CarWithEngine;
import org.adee.samples.shallowCopy.Engine;

public class DeepCopyDemo {

	public static void main(String[] args) {
		// original object
		CarWithEngine original = new CarWithEngine("Honda", "Brio",
                                        new Engine("ford-engine"));

		// copied object created with copy constructor
		CarWithEngine copied = new CarWithEngine(original);
		
		// modify the engine of the copied object
		// this will not impact the original object 
		// as it is deep copied.
		copied.getEngine().setEngineModel("fakeEngine");
		
		// this will print ford-engine
		System.out.println(original.getEngine().getEngineModel());
	}
}

4. Copy Constructors with Inheritance

Let’s see an example with inheritance. Consider the following inheritance model where a class Hatchback extends class Car and both classes define their own copy constructors as shown below.

Hatchback.java

package org.adee.samples;

public class Hatchback extends Car {

	private int seats;

	public Hatchback(String make, String model, int seats) {
		super(make, model);
		this.seats = seats;
	}

	// copy constructor in sub-class
	public Hatchback(Hatchback hatchback) {
		super(hatchback.getMake(), hatchback.getModel());
		this.seats = hatchback.getSeats();
	}

	public int getSeats() {
		return seats;
	}

	public void setSeats(int seats) {
		this.seats = seats;
	}
}

Initializing a sub-class object(Hatchback) from a super-class reference(Car) with a copy constructor will result in a casting issue. You need to explicitly typecast it to the sub-class type to use this of the sub-type.

Casting issue

		Car hatchback = new Hatchback("Toyota", "Etios", 5);
		// The constructor Hatchback(Car) is undefined compiler error 
		Car cloneCar = new Hatchback((Hatchback) hatchback);

Also, you might end up in a java.lang.ClassCastException at runtime if you are not careful about the input object to the sub-class. It must be an instance of the Hatchback class.

ClassCastException

		Car car = new Car("Honda", "Jazz");
		Car cloneCar2 = new Hatchback((Hatchback) car);

5. Copy Constructor or Object.clone() method?

Java also provides the clone method to create and return a copy of the object on which it is invoked. Using a copy constructor instead of the clone() method has the following benefits:

  1. Unlike the clone() method, this does not enforce to implement the Cloneable marker interface or any such interface.
  2. The clone() method returns a reference of type java.lang.Object and one have to manually typecast it to the desired type.
  3. There is no need to handle CloneNotSupportedException when using a copy constructor.
  4. When it comes to the final fields of a class, a copy constructor will allow to set its value because after all, it is a constructor. However the clone() method will never allow us to do so.

6. Summary

To summarise let’s recapitulate what we discussed so far w.r.t.

  • Values of primitive types (int, float, double, etc) are copied just the same.
  • References to immutable types (e.g. String) are also copied just as it is. Although the original and the copied objects point to the same memory location, the referred object is immutable and never changes.
  • References to mutable types (e.g. Date, List, etc.) must be copied deeply. Otherwise the original and the copied object will refer exactly to the same memory location of the mutable field which is undesirable.

8. Download the source code

In this tutorial, we learned about the copy constructor, when to use it, and how to use it.

Download
You can download the full source code of this example here: Copy Constructor Java Example

Last updated on Jul. 26th, 2021

Anmol Deep

Anmol Deep is a senior engineer currently working with a leading identity security company as a Web Developer. He has 8 years of programming experience in Java and related technologies (including functional programming and lambdas) , Python, SpringBoot, Restful architectures, shell scripts, and databases relational(MySQL, H2) and nosql solutions (OrientDB and MongoDB). He is passionate about researching all aspects of software development including technology, design patterns, automation, best practices, methodologies and tools, and love traveling and photography when not coding.
Subscribe
Notify of
guest

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

0 Comments
Inline Feedbacks
View all comments
Back to top button