Domanda

I was reading Joshua Bloch's Effective Java. In there he talks about not using the Clonable interface. I'm a bit of a noob so my question is, whats a use-case for when cloning would be required in code? Could someone give a sticky example so I can grasp the concept?

È stato utile?

Soluzione

A clone() interface provides a mechanism to create a "shallow" copy of an object. That is, by default, more memory would be allocated for the copy, and each part of the original would be copied into the copy. In contrast, a simple assignment of an object instance to a variable would result in an additional reference to the same object. While the cloned object is itself a true copy, its contained elements are by default references to the ones referred to from the original. If a true "deep" copy is needed, the clone() method would need to be specialized to create clones of its members as well.

One possible use case for a clone() interface would be for implementing a version history of an object to allow a rollback to an older version of it. This could be used in transactional systems, like databases.

Another use case would be for implementing copy-on-write, and this can be useful when the user of the object is only provided a read-only version of the object.

* Description of clone corrected, with thanks and kudos to newacct.

Altri suggerimenti

From Wikipedia on the Prototype pattern,

... you would need to clone() an Object when you want to create another Object at runtime that is a true copy of the Object you are cloning. True copy means all the attributes of the newly created Object should be the same as the Object you are cloning. If you could have instantiated the class by using new instead, you would get an Object with all attributes as their initial values. For example, if you are designing a system for performing bank account transactions, then you would want to make a copy of the Object that holds your account information, perform transactions on it, and then replace the original Object with the modified one. In such cases, you would want to use clone() instead of new.

One example is defensive copying of parameters (Effective Java Item 39: Make defensive copies when needed)

class X {
  private byte[] a;

  X(byte[] a) {
      this.a = a.clone();
  }
...

There are very good reasons for cloning an object in Java. Consider the following code:

MyObj first = new MyObj(someOwner, someTag, someInt);
MyObj second = first;

In this case, we are simply copying the reference (memory address) of that object. The variables first and second both refer to the same instance of the MyObj class. What the clone() method is supposed to achieve is what is called a deep copy. This is, of course, implementation dependent: the developer of the clone method needs to ensure that a deep copy is actually what is being achieved. So, the code:

MyObj third = (MyObj) first.clone();

What clone() does in this case is go through all of first's instance members and copy/clone those, and uses those values to create an entirely new MyObj instance. It then returns a reference to the new instance, so third is a copy of first, not just a reference to the same instance.

In response to your question in the comments, it depends on your implementation whether or not clone makes new clones of member variables, or simply copies the reference over. Consider the following implementation of the MyObj example class, supposing there also exist classes Person and NameTag. If you clone a MyObj object, you might want the new clone to refer to the same Owner instance as the original, but make a deep copy of the NameTag (this is just an example using imaginary classes, of course). This would represent a one-to-one relationship between MyObj and NameTag instances, and a one-to-many relationship between Owner and MyObj instances. The following code considers both cases mentioned in your question:

class MyObj implements Cloneable {

    private Person owner;
    private NameTag tag;
    private int size; 

    public MyObj(Person owner, NameTag tag, int size) {
         this.owner = owner;
         this.tag = tag;
         this.size = size;
    }

    public Object clone() {

        MyObj result;

        //call all super clone() methods before doing class specific work
        //this ensures that no matter where you are in the inheritance heirarchy,
        //a deep copy is made at each level.
        try {
            result = (MyObj) super.clone();
        }
        catch (CloneNotSupportedException e) {
            throw new RuntimeException
            ("Super class doesn't implement cloneable");
        }

        //work specific to the MyObj class
        result.owner = owner;     //We want the reference to the Person, not a clone
        result.tag = tag.clone()  //We want a deep copy of the NameTag
        result.size = size;       //ints are primitive, so this is a copy operation    

        return result;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top