Link to home
Start Free TrialLog in
Avatar of gudii9
gudii9Flag for United States of America

asked on

Deep Clone Example

Hi,

I am working on below example

http://javapapers.com/core-java/java-clone-shallow-copy-and-deep-copy/

i have not understood what author is mentioning as below relating to deep clone.
Deep Copy
When you need a deep copy then you need to implement it yourself. When the copied object contains some other object its references are copied recursively in deep copy. When you implement deep copy be careful as you might fall for cyclic dependencies. If you don’t want to implement deep copy yourselves then you can go for serialization. It does implements deep copy implicitly and gracefully handling cyclic dependencies.






One more disadvantage with this clone system is that, most of the interface / abstract class writers in java forget to put a public clone method. For example you can take List. So when you want to clone their implementations you have to ignore the abstract type and use actual implementations like ArrayList by name. This completely removes the advantage and goodness of abstractness.

When implementing a singleton pattern, if its superclass implements a public clone() method, to prevent your subclass from using this class’s clone() method to obtain a copy overwrite it and throw an exception of type  CloneNotSupportedException.

Note that clone is not for instantiation and initialization. It should not be synonymously used as creating a new object. Because the constructor of the cloned objects may never get invoked in the process. It is about copying the object in discussion and not creating new. It completely depends on the clone implementation. One more disadvantage (what to do there are so many), clone prevents the use of final fields. We have to find roundabout ways to copy the final fields into the copied object.


My code looks as below

class Employeee implements Cloneable {
	private String name;

	private String designation;

	public Employeee() {
		this.setDesignation("Programmer");
	}

	public String getDesignation() {
		return designation;
	}

	public void setDesignation(String designation) {
		this.designation = designation;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Object clone() throws CloneNotSupportedException {
		/*
		 * CloneExample2222 copyObj = new CloneExample2222();
		 * copyObj.setDesignation(this.designation); copyObj.setName(this.name);
		 * return copyObj;
		 */
		return super.clone();
	}
}

public class CloneExample2222 {
	public static void main(String arg[]) {
		Employeee jwz = new Employeee();
		jwz.setName("Jamie Zawinski");
		try {
			Employeee joel = (Employeee) jwz.clone();
			System.out.println(joel.getName());
			System.out.println(joel.getDesignation());
		} catch (CloneNotSupportedException cnse) {
			System.out.println("Cloneable should be implemented. " + cnse);
		}
	}
}

Open in new window



I got output as below which is also not clear to me

Jamie Zawinski
Programmer

How is implementign cloneable different from sefializable?
please advise
Any links resources ideas highly appreciated. Thanks in advance
Avatar of dpearson
dpearson

The main point to grasp is the difference between a shallow copy and a deep copy.

This only matters when the object being copied, contains other objects.
E.g.

public MyList {
    private List<String> m_Names ;

    public MyList(List<String> names) { m_Names = names ; }

    public void remove(String name) { m_Names.remove(name) ; }
    public String toString() { return "List=" + m_Names ; }

    public MyList createShallowCopy() {
       MyList newList = new MyList(m_Names) ;
    }

    public MyList createDeepCopy() {
       MyList newList = new ArrayList<String>() ;
       newList.addAll(this.m_Names) ;
       return newList ;
    }
}

Open in new window


Take a look at the differences between createShallowCopy() and createDeepCopy().

Imagine we have:
List<String> names = new ArrayList<String>() ;
names.add("Sam") ;
names.add("Tony") ;
names.add("Lisa") ;
MyList start = new MyList(names) ;

Now if we create a shallow copy:
MyList shallowCopy = start.createShallowCopy() ;
MyList deepCopy = start.createDeepCopy() ;

the list *inside* shallowCopy and start are both the same list.

So if we now do:
start.toString() returns "Sam, Tony, Lisa"
shallowCopy.toString() returns "Sam, Tony, Lisa"
deepCopy.toString() returns "Sam, Tony, Lisa"

start.remove("Sam") ;
start.toString() returns "Tony, Lisa".

But:
shallowCopy.toString() also returns "Tony, Lisa" because the list inside shallowCopy is the same list as the one inside start.

That may be OK, or it may not.  But it's different from deep copy:

deepCopy.toString() returns "Sam, Tony, Lisa" because it contains a different internal list than start does.  So when we removed "Sam" it didn't affect deep copy.

Does that all make sense?

The rest of the article just talks about specifically what clone does (a  shallow copy) and some of the reasons why it's usually best avoided.

Also if you implement serializable you can potentially use that to make a copy since you serialize the data into some form and then deserialize that to get back to the original.  If you do that without throwing away the original then it produces a copy.  So it's one way to copy an object.

Doug
Avatar of gudii9

ASKER

deepCopy.toString() returns "Sam, Tony, Lisa" because it contains a different internal list than start does.  So when we removed "Sam" it didn't affect deep copy.


How and where do i see to find it contains different internal list. What is the name of internal list deepCopy, shallowCopy has. For me it seems both has same list as below

   public MyList createShallowCopy() {
       MyList newList = new MyList(m_Names) ;
    }

    public MyList createDeepCopy() {
       MyList newList = new ArrayList<String>() ;
       newList.addAll(this.m_Names) ;
       return newList ;
    }
Please advise
Oops - my code has some typos which may have thrown you off.

Should be:

 public MyList createShallowCopy() {
       MyList newList = new MyList(m_Names) ;
       return newList ;
    }

    public MyList createDeepCopy() {
       List<String> newInternalList = new ArrayList<String>() ;
       newInternalList.addAll(this.m_Names) ;
       MyList newList = new MyList(newInternalList) ;
       return newList ;
    }

Open in new window


Can you see now how the deep copy is creating a newInternalList while the shallow copy doesn't do that?

Doug
Avatar of gudii9

ASKER

deepCopy.toString() returns "Sam, Tony, Lisa" because it contains a different internal list than start does.  So when we removed "Sam" it didn't affect deep copy.




 List<String> newInternalList = new ArrayList<String>() ;
       newInternalList.addAll(this.m_Names)


I wonder why  newInternalList  again contains m_Names similar to Shallow Copy.

I did not get the gist of removing list member etc and how it is different in both cases?

I am still not very clear. Can you please post complete code which i can run and see. Please advise
Avatar of gudii9

ASKER

Can you see now how the deep copy is creating a newInternalList while the shallow copy doesn't do that?


I do not understand clearly.

I modified program as below

 public class MyList {
    private List<String> m_Names ;

    public MyList(List<String> names) { m_Names = names ; }

    public void remove(String name) { m_Names.remove(name) ; }
    public String toString() { return "List=" + m_Names ; }

   public MyList createShallowCopy() {
       MyList newList = new MyList(m_Names) ;
       return newList ;
    }

    public MyList createDeepCopy() {
       List<String> newInternalList = new ArrayList<String>() ;
       newInternalList.addAll(this.m_Names) ;
       MyList newList = new MyList(newInternalList) ;
       return newList ;
    }

}

Open in new window


I see compilation errors like
List cannot be resolved to a type at line 2 and following lines where m_names is used. can you please post complete code. please advise
Add this to the top of the file - so it can resolve List:

import java.util.*;

Doug
Avatar of gudii9

ASKER

import java.util.ArrayList;
import java.util.List;

public class MyList {
    private List<String> m_Names ;

    public MyList(List<String> names) { m_Names = names ; }

    public void remove(String name) { m_Names.remove(name) ; }
    public String toString() { return "List=" + m_Names ; }

   public MyList createShallowCopy() {
       MyList newList = new MyList(m_Names) ;
       return newList ;
    }

    public MyList createDeepCopy() {
       List<String> newInternalList = new ArrayList<String>() ;
       newInternalList.addAll(this.m_Names) ;
       MyList newList = new MyList(newInternalList) ;
       return newList ;
    }

}

Open in new window



It fixed the compilation error once i imported list and array list.

But there is no main method or anything like that. How to run. Can you please provide me complete code to understand this example
ASKER CERTIFIED SOLUTION
Avatar of dpearson
dpearson

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of gudii9

ASKER

It makes much more sense. I am still trying to digest.

So the link

http://javapapers.com/core-java/java-clone-shallow-copy-and-deep-copy/


author program is only clone example right which internally uses shallow copy not deep copy.

>>Employee joel = (Employee) jwz.clone();

author in this example using shallow copy(clone) cloned  jamie(jwz) into joe1 object right.
so joe1 name , designation also became same as jamie. So output is

Jamie Zawinski

Programmer


Is my understanding is correct?
Yes that's right.  Clone() is a built-in method but it only does a shallow copy.  If you want a deep copy you need to write it yourself.

(There are lots of problems with clone().  The usual advice is to avoid it because unless you're very careful with it, it will do something different from what you want - like this example of a shallow copy).

Doug