scottstots
scottstots

Reputation: 193

Deep cloning an object of a class in java

I have an object game of my Game.java class in my main class. I need to create a copy (game_copy) of game object such that when I make changes to the board of game_copy, the board of game does not get changed. I make this game_copy object as:

Game game_copy = new Game() ;
game_copy = game.clone() ;

But still when I make changes to the board of game_copy, the board of game gets changed as they still share a reference. How do I sort out this problem? Here are my Game and rplayer classes that I am using:

class Game implements Cloneable{
     int n;
     ArrayList<ArrayList<String>> board;
     rplayer [] players;
     int a ;

    public Game(int n){
      this.n=n;
      players = new rplayer[2] ;
      players[0]=new rplayer(this.a);
      players[1]=new rplayer(this.a);
      board= new ArrayList<ArrayList<String>>();
       for(int p= 0 ; p < n*n ; p++){
        ArrayList<String> r = new ArrayList<String>() ;
        board.add(r) ;
       }
     }

   public Game clone(){ 
    Game gm ;
    try { 
        gm =  (Game) super.clone(); 
    } 
    catch (CloneNotSupportedException e) { 
        System.out.println (" Cloning not allowed. " );
         return null; 
    }
    return gm
 }
}

class rplayer{
    int a;
    public rplayer(int a){
      this.a=a;
    }
}

This is what I tried before trying to use .clone() method, but the idea of making a copy constructor didn't work either. Both the objects were always related.

public Game(Game g){
this.n=g.n;
this.players = new rplayer[2] ;
rplayer[] temp_players = new rplayer[2] ;
temp_players = g.players;
this.players = temp_players ;
this.board= new ArrayList<ArrayList<String>>();
ArrayList<ArrayList<String>> temp_board = new ArrayList<ArrayList<String>>() ;
    for(int p= 0 ; p < n*n ; p++){
        ArrayList<String> r = new ArrayList<String>() ;
        board.add(r) ;
        temp_board.add(r) ;
    }
    temp_board = g.board ;
    board= temp_board;
}

Upvotes: 0

Views: 6915

Answers (3)

nasukkin
nasukkin

Reputation: 2540

Implementing the Cloneable interface is generally not recommended. A few excerpts from Joshua Bloch's Effective Java (2nd Edition):

Override clone judiciously. The Cloneable interface was intended as a mixin interface for objects to advertise that they permit cloning. Unfortunately, it fails to serve this purpose. Its primary flaw is that it lacks a clone method, and Object's clone method is protected.

Josh then goes on to explain the many shortcomings of the Cloneable interface and the clone method. What he suggests as an alternative is that you make a "copy constructor" that copies the state of one object into your new copy, defensively copying the things that need protection. In your case, a copy constructor would probably look something like this...

public Game(Game original) {
    if (null == original) 
        throw new IllegalArgumentException("Game argument must not be null for copying");

    // int primitives can be copied by value harmlessly.
    this.n = original.n;
    this.a = original.a;

    // Collections require special protection, however, to ensure that
    // manipulating one game's state doesn't manipulate the other.
    ArrayList<ArrayList<String>> boardCopy = new ArrayList<>();
    for (ArrayList<String> subList : original.board) {
        // Fortunately, Strings are immutable, so we don't need to
        // manually copy them. And ArrayList has a copy constructor
        // of its own, so that's handy!
        ArrayList<String> subCopy = new ArrayList<>(subList); 
        boardCopy.add(subCopy);
    }
    this.board = boardCopy;

    this.players = new rplayer[original.players.length];
    for (int i = 0; i < original.players.length; i++) {
        rplayer player = original.players[i];
        // If--and ONLY if--the rplayer class is immutable, this
        // is safe; otherwise you need to copy each player on your
        // own!
        this.players[i] = player;
    }
}

The idea here is that you want to completely eliminate any "spooky action at a distance"; that is, make sure it's impossible for changing the state of Game A from indirectly changing the state of Game B. If something is immutable (Strings, wrapped primitives, your own immutable class), you can re-use the references between them. Otherwise, you need to copy the state of those objects to brand new instances that your copy will use.

Upvotes: 0

seal
seal

Reputation: 1143

What I have found to make deep copy of a complex object by doing mark the object with Serializable interface and then serialize the object in memory. As your Game object has reference to another object and that object also has the reference to others it would be better to clone the object through serialization. This will ensure you that you will get absolutely different object. Java clone will not serve the purpose as the objects are getting complicated.

here is the sample code how to serialize object in memory.

class Game implements Serializable {

........
........
........

public Game makeClone() throws IOException, ClassNotFoundException {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(outputStream);
    out.writeObject(this);

    ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
    ObjectInputStream in = new ObjectInputStream(inputStream);
    Game copied = (Game) in.readObject();
    return copied;
}

}

Upvotes: 0

Christopher Schneider
Christopher Schneider

Reputation: 3915

Per the Javadocs:

Otherwise, this method creates a new instance of the class of this object and initializes all its fields with exactly the contents of the corresponding fields of this object, as if by assignment; the contents of the fields are not themselves cloned. Thus, this method performs a "shallow copy" of this object, not a "deep copy" operation.

In other words, a new object IS created, but the references inside the object still point to the same member variables. You need to make copies of the contents inside the object as well as the object itself.

https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html

Upvotes: 1

Related Questions