How to Implement Deep Copy and Shallow Copy In Java

  • Post last modified:June 6, 2023
  • Reading time:5 mins read

Implementing deep copy and shallow copy in Java

Introduction

  • In the previous article, we explained the concept of deep copy and shallow copy
  • This article focuses on its implementation in Java.

 Java Cloneable Interface & Clone() method

  • Java provides a cloneable interface that requires each implementing class to have a clone() method.
  • The default nature of the clone() is a shallow copy, hence we need to implement the clone in such as way that it deep copies each reference type instead of copying references.
  • Let’s see some code examples with a cloneable interface and clone() method

Shallow Copy Example

  • The user class implements the clone() method and returns the clone instance by calling super.clone().
  • Basically, it will clone all the properties of the user using either the value types or reference types.
  • In shallow copy, If the property is an object type, it will only get the reference. for example in the User class, the Address object is copied as a reference copy and not the value copy.
  • But String (Since it’s immutable ) and int (Since it is primitive) are copied as value types.
public class User implements Cloneable{
    private String name;
    private Address address;
    private int age;
    // getters, setters
    
    @Override
    public User clone() {
        try {
            User clone = (User) super.clone();
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
  • Similarly, we have an Address class that implements the clone() method.
class Address implements Cloneable{
    private String streetName;
    private String zipCode;
    private String cityName;
    private String country;
    
    @Override
    public Address clone() {
        try {
            Address clone = (Address) super.clone();
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
  • Now if we test cloning the user object, we will get the shallow copy of the user object.
public static void main(String[] args) {
    System.out.println("Shallow Copy Of Address: ");
    User user =  build();;
    User userClone = user.clone();
    System.out.println("user object hashcode : " +user.hashCode());
    System.out.println("userClone object hashcode :" +userClone.hashCode());
    System.out.println("user address object hashcode :" +user.getAddress().hashCode());
    System.out.println("userClone address object hashcode: " +userClone.getAddress().hashCode());
}

public static User build() {
        User user = new User();
        user.setName("sam");
        user.setAge(10);
        Address address = new Address();
        address.setCityName("San Mateo");
        address.setCountry("USA");
        address.setZipCode("94402");
        address.setStreetName("430 Station Park Circle");
        user.setAddress(address);
        return user;
}

Output

  • As we can see the hashcode of the user object is different but the hashcode of the address is the same. 
  • This represents the user.clone() only clones the properties as shallow type. 

Deep Copy Example

  • In the below example, we are implementing the clone() method as usual, but the main difference is that we are also cloning the address so that the cloning user returns the deep copy of the object and not the shallow copy for the address.
public class User implements Cloneable{
private String name;
    private Address address;
    private int age;
    
   // getters , setters
       @Override
    public User clone() {
        try {
            User user = (User) super.clone();
            user.setAddress(user.getAddress().clone()); // deep copy of reference
            return user;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
  • Address class clone() implementation doesn’t change since it doesn’t have a custom data type and String is immutable so it will not return shallow reference.
class Address implements Cloneable{
    private String streetName;
    private String zipCode;
    private String cityName;
    private String country;
   // getters, setters
   @Override
    public Address clone() {
        try {
            Address clone = (Address) super.clone();
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
  • Now if we test, this time we will get a deep-cloned user object.
public static void main(String[] args) {
    System.out.println("Deep Copy Of Address: ");
    User user =  build();;
    User userClone = user.clone();
    System.out.println("user object hashcode : " +user.hashCode());
    System.out.println("userClone object hashcode :" +userClone.hashCode());
    System.out.println("user address object hashcode :" +user.getAddress().hashCode());
    System.out.println("userClone address object hashcode: " +userClone.getAddress().hashCode());
}

public static User build() {
    User user = new User();
    user.setName("sam");
    user.setAge(10);
    Address address = new Address();
    address.setCityName("San Mateo");
    address.setCountry("USa");
    address.setZipCode("94402");
    address.setStreetName("430 Station Park Circle");
    user.setAddress(address);
    return user;
}

Output

  • As we can see hashcode for the user object and its clone is different, so the hashcode of the address.

Conclusion

  • In this article, we implemented deep copy and shallow copy using the Java Cloneable interface and clone() method.
  • Shallow copy and deep copy both are useful depending on the use case.

Before You Leave

Leave a Reply