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.
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:
- Unlike the clone() method, this does not enforce to implement the Cloneable marker interface or any such interface.
- The clone() method returns a reference of type java.lang.Object and one have to manually typecast it to the desired type.
- There is no need to handle CloneNotSupportedException when using a copy constructor.
- 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.
7. More articles
- Java Constructor Example (with video)
- Java Tutorial for Beginners (with video)
- 150 Java Interview Questions and Answers – The ULTIMATE List (PDF Download & video)
- Best Way to Learn Java Programming Online
8. Download the source code
In this tutorial, we learned about the copy constructor, when to use it, and how to use it.
You can download the full source code of this example here: Copy Constructor Java Example
Last updated on Jul. 26th, 2021