Nirmalya
Nirmalya

Reputation: 420

Shallow cloning and deep cloning

I am revisiting some of the fundamental aspects of Java and have some doubts regarding shallow cloning and deep cloning. I know them in details and aware of the internals. But I stumbled on this simple exercise -

Department class -

public class Department {
    String deptName;

    public String getdName() {
        return deptName;
    }

    public void setdName(String dName) {
        this.deptName = dName;
    }

    public Department() {
        super();
    }

    public Department(String dName) {
        super();
        this.deptName = dName;
    }
}

Employee class -

public class Employee implements Cloneable{
    int empNo;
    String empName;
    Department dept;

    public int getEmpNo() {
        return empNo;
    }
    public void setEmpNo(int empNo) {
        this.empNo = empNo;
    }
    public String getEmpName() {
        return empName;
    }
    public void setEmpName(String empName) {
        this.empName = empName;
    }
    public Department getDept() {
        return dept;
    }
    public void setDept(Department dept) {
        this.dept = dept;
    }
    public Employee(int empNo, String empName, Department dept) {
        super();
        this.empNo = empNo;
        this.empName = empName;
        this.dept = dept;
    }
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

The main class -

public class ShalowCopyTest {

    public static void main(String args[]) {
        Department dept1 = new Department("Development");
        Department dept2 = new Department("Testing");
        Employee emp1 = new Employee(10, "Peter", dept1);
        Employee emp2 = null;
        try {
            emp2 = (Employee) emp1.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        System.out.println(emp1.getDept().getdName());
        System.out.println(emp2.getDept().getdName());

        System.out.println("Now changing emp1.dept");
        //emp1.setDept(dept2); //This is deep cloning - why?
        emp1.getDept().setdName("Testing"); //This is shallow cloning
        System.out.println(emp1.getDept().getdName());
        System.out.println(emp2.getDept().getdName());
    }
}

You can see that if I use emp1.setDept(dept2), changing the dept of emp1 has no effect on emp2. So this is deep cloning. But changing the dName by just emp1.getDept().setdName("Testing") is making the dName change for emp2 as well. So this is shallow cloning. Why these two lines are making a difference? Why they are not the same? Thanks.

Upvotes: 0

Views: 325

Answers (3)

Corovus
Corovus

Reputation: 38

Your example is a shallow and now a deep copy, it only seems like you have a deep copy. Let me try to explain why:

The object you are cloning (Employee) does have a complex datatype (Department). If you clone an object that contains other objects then you only copy the reference and not the actual object.

Let’s take a look at your code:

emp1.setDept(dept2)

In this case you are not altering the actual instance (which would have an impact on both, the clone and the original), but you are assigning a new object to the dept instance of emp1. Therefore you now have different instances of dept in emp1 and emp2. That is why it seems like you have a deep copy.

Let’s say you would have another instance variable inside of your Department with all the necessary methods:

public class Department {
    String deptName;
    int someNumber;

    public void setNumber(int number){
    someNumber=number;
    }

    public int getNumber(){
    return someNumber
    }


    public Department(String dName, int number){
    super();
    deptName = dName;
    someNumber = number;
    }
    }

In you main Method you do something like that:

Department dept1 = new Department(“Development”, 50);
Employee emp1 = new Employee(10, “Peter”, dept1);
Employee emp2 = null;
emp2 = (Employee) emp1.clone();
System.out.println(emp1.getDept().getNumber; // Output: 50
System.out.println(emp1.getDept().getNumber; // Output: 50
//Now we change the instance variable of dep1 inside emp1
emp1.getDept().setNumber(100);
//Now print the numbers of both employees again
System.out.println(emp1.getDept().getNumber; // Output: 100
System.out.println(emp1.getDept().getNumber; // Output: 100

As you can see, it is a shallow and not a deep copy. The only reason why emp1.getDept().setdName(“Testing”) is just altering the original and not the clone is because Strings are immutable. Every time you change a String you get a new String instance back, therefore it doesn’t have any impact on the original String instance after cloning the object.

To make your example a deep copy you have to adjust your clone method like so: (I made some small adjustments as well so the datatype is matching and you don’t need a try/catch anymore)

@Override
public Employee clone(){
Employee clone = null;

try{
clone = (Employee)super.clone();
clone.Department = Department.clone(); // This is the important line!! You need to clone the Department Object aswell. 
}catch (CloneNotSupportedException e){
e.printStackTrace();
    }
return clone;
}

Of course you have to implement the clonable Interface and the clone method inside if your Department Class aswell to make it work.

Upvotes: 1

Luisa Bradusca
Luisa Bradusca

Reputation: 88

I noticed you are using same example as on this link: A Guide to Object Cloning in Java

Probably you already know that the default implementation of super.clone() does a Shallow Copy instead of a Deep Copy.

In this case, both object instances: emp1 and emp2 will contain references that points to the same places in memory. Referring to Department instances, this is how it references in memory after emp1 object is cloned: enter image description here Both refer to the same place in memory, that contains an immutable String: "Development".

By setting:

emp1.setDept(dept2);

it changes the reference of emp1 to the new object dept2: enter image description here

In this case you will see it as a Deep Copy but in fact is not. It is just that the emp1 changed it's reference to a new place in memory.

In the other case, when you set:

emp1.getDept().setdName("Testing");

You will change the Departman's name to "Testing" - which will be visible to both object instances.

Please let me know if you have any more questions.

Upvotes: 1

slim
slim

Reputation: 41223

Since you are using super.clone() and super is an Object, you get a shallow clone in which every field gets copied.

So if you start with:

emp1 -> dept1 -> dname1

then emp2 = emp2.clone(), you get:

emp1 -> dept1 -> dname1 ^ emp2 ----/

i.e. they are both pointing at the same Department object.

If you then do dept1.name = dname2, you only affect dept1, so you get:

emp1 -> dept1 -> dname2 ^ emp2 ----/

... and emp1.getDepartment().name = dname2 has exactly the same effect -- it doesn't matter how you get at the object.

If you now do emp2.department = dept2, it does not affect emp1, so you end up with:

emp1 -> dept1 -> ... // unchanged emp2 -> dept2 -> ... // newly assigned

To get deep cloning you would need to write your own clone routine, which clone each level.

This is easy to get wrong. Better to get in the habit of using immutable objects, in which shallow copies "just work" -- because you can never change a field, you can't accidentally affect an object that's sharing the same object as the one you're working with.

Upvotes: 1

Related Questions