We can assign reference of an object to another, provided
each of them satisfies an “is-a”
relationship or of same type. For example, a Dog object can hold the reference
of another Dog object regardless both have different states. Similarly an
Animal object can hold the reference of a Dog or Cat object, if they satisfies
an “is-a” relationship. But hold on,
these assignments do not help you to create a duplicate copy of the object.
It’s nothing but different pointers pointing to the same memory location.
Java provides API to clone an object. Using this API one can
get a copy of an object to another. To achieve this, proposed class should
implement Clonable interface (else
JVM will throw CloneNotSupportedException
during runtime). The rationale behind cloning is to get an independent copy of
an object. The Object class has a clone() method which takes care of this operation.
This method is protected by nature
and returns an Object, when called. You can call this method on an object if you intend
to get a copy of that object; however you have to make sure that the class of
the object implements a Clonable
interface. The Clonable interface is
a marker interface. A marker interface by definition is an empty interface that
does not contain any method and used to serve special purposes. Clonable interface tells the JVM that
the caller may ask to give a new copy of an object of this class, so be
prepared or in other words this class can be cloned by the caller. Is not it pretty!
Look at the below example,
I have created a play list where I can add my favorite
songs, this class is Clonable so that if I want to get a copy of this play list
I can get that easily and save that somewhere else using some serializable class (a class whose
objects can be saved to secondary memory, to achieve that I have make this
class serializable. Just forget it for now!).
public class PlayList
implements Cloneable {
private ArrayList<Song> songs;
public PlayList() {
this.songs = new
ArrayList<Song>();
}
public String displaySongs() {
return songs.toString();
}
public void addSongs(Song song) {
songs.add(song);
}
public void removeSong(Song song) {
songs.remove(song);
}
@Override
protected Object clone() throws
CloneNotSupportedException {
return super.clone();
}
}
I have overridden the clone() method
and I just called the super class to invoke it. If you do not override this
method you may not be able to clone any object of this class, because if you
try so compiler will not allow you to do that.
public class Song {
private String name;
public Song(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return getName();
}
@Override
public boolean equals(Object obj) {
if(obj != null && obj
instanceof Song) {
return ((Song)
obj).getName().equals(name);
}
return false;
}
}
I think we are good now and can start writing the main method to run
the application with some songs added into the play list. I have created an
original play list (playList) and
copied that list to another play list (copied_PlayList).
Isn’t it awesome, I have two play lists now.
public class Main {
public static void main(String[] args)
throws CloneNotSupportedException {
Song song1 = new Song("Hotel California");
Song song2 = new Song("Mama I'm
coming home");
Song song3 = new Song("November
Rain");
PlayList playList = new PlayList();
playList.addSongs(song1);
playList.addSongs(song2);
playList.addSongs(song3);
System.out.println("Original List:
" + playList.displaySongs());
PlayList copied_PlayList = (PlayList)
playList.clone();
System.out.println("Copied List:
" + copied_PlayList.displaySongs());
}
}
And here it goes; I can see both lists when I run the program,
Original List: [Hotel
California, Mama I'm coming home, November Rain]
Copied List: [Hotel
California, Mama I'm coming home, November Rain]
Let me add one more song into the original list and see, whether I get
two different list or not,
Song
song4 = new Song("Sweet child of mine");
playList.addSongs(song4);
System.out.println("Original List:
" + playList.displaySongs());
System.out.println("Copied List:
" + copied_PlayList.displaySongs());
When I run the program I see below output,
Original List:
[Hotel California, Mama I'm coming home, November Rain, Sweet child of mine]
Copied List:
[Hotel California, Mama I'm coming home, November Rain, Sweet child of mine]
What the hell, that’s not so awesome. I am getting the same play list
twice, which I do not want. Any modification to the original list should not
affect the copied list. It’s just a copied list from the original old list.
I think I should cool down and understand the principles behind cloning
an object. When an object is cloned it can be cloned using two principles or
techniques,
1.
Swallow copy
2.
Deep Copy
Oh boy, I should have thought of the before, I have to change the
application now. Anyway let me tell you what each means,
1.
Swallow copy, It says if you are cloning an
object JVM will copy the structure not the reference variables, meaning it will
not copy the whole object graph unless you are using some immutable objects.
The reference variables of both the objects will refer to the same variable
references.
In my example, Play list class
has an array list (songs) reference
that holds the entire song objects that I have added so far. As per the
principle of swallow copy this list will be shared among the original and the
copied list, hence any changes to this list will be reflected to both the objects,
and that’s what we saw. So, looks like we just implemented swallow copy.
2.
Deep copy, It’s the opposite of swallow copy.
Deep copy helps you to get a completely new copy of an object. Look at the
below Song list class, inside the clone method I have created a new object of
Song list and I have added the song list.
@Override
protected Object clone() throws
CloneNotSupportedException {
PlayList playList = new PlayList();
playList.songs = new
ArrayList<Song>();
playList.songs.addAll(this.songs);
return playList;
}
If I run the main
program now I can see below,
Original List: [Hotel
California, Mama I'm coming home, November Rain]
Copied List: [Hotel
California, Mama I'm coming home, November Rain]
Original List: [Hotel
California, Mama I'm coming home, November Rain, Sweet child of mine]
Copied List: [Hotel
California, Mama I'm coming home, November Rain]
Phew! that was not that hard. So we have to remember two things here,
Swallow copy and Deep copy before designing a class which is Clonable. It’s all
depends on the requirement; however we should be able to choose the best one as
per our need. Keep this in mind that primitive types and immutable objects are
always deep copied under any circumstances, if they are not forced to change.
Comments
Post a Comment