Cloning in Java

て烟熏妆下的殇ゞ 提交于 2019-12-02 23:06:52

In short

  • o.clone() calls Object.clone(), which makes a memory copy of o. Thereafter the copied references cannot be changed for final field, so we have involuntary aliasing;
  • the unwanted aliasing and mutability do not go well with each other: If o changes, the clone involuntarily changes, too;
  • the statements in the blog post you cite are copied from this much better blog post, which attests the statements with good code examples.

Details and examples about cloning via `Object.clone()'

I believe the article is talking about clone() via clone-chaining (via super.clone()), such that eventually Object.clone() is called, which makes a flat memory copy of the object being cloned via native code.

Let's say we have the following example (from the blog post mentioned below):

public class Person implements Cloneable
{
    private final Brain brain; // brain is final since I do not want 
                // any transplant on it once created!
        // ...
}

and

person2 = (Person) person1.clone();

then person2 has the same memory-part for the field brain as person1, i.e. both hold the same reference to the same brain. Then since Person objects are mutable, they can learn things:

person1.learn(dvorakTyping);

then magically person2 can type on a dvorak keyboard as well. With immutable objects, this problem doesn't occur (though cloning's still problematic since final fields can still not be initialized via parameters, as in constructors).

Cloning via constructor calls

The reason for my very first half sentence: You can implement clone via calling one of the object's constructors. Some people claim this is against clone's contracts, but it isn't. Here's a good blog post about why to call a constructor within clone (one main reason being those final fields).


Update

Reading Hemal's comment on mre's answer, I did glance over the blog post the question cited, and it turns out, the post copied some sentences from the blog post I cited, but without the very good code examples. LOL.

I don't understand it either, except that a class cannot implement a well behaved clone method if all its fields don't also have a well behaved clone methods.

Not that I recommend it but one can use sun.misc.Unsafe to overwrite a value of a final field. It is highly not recommended to use this class but this post is not about that (). A sample code to overwrite a value of a final field:

    public class FinalClone
    implements Cloneable {
private final FinalClone finalField;

public FinalClone(FinalClone finalField) {
    this.finalField = finalField;
}

@Override
protected FinalClone clone()
        throws CloneNotSupportedException {

    final FinalClone result = (FinalClone) super.clone();

    if (finalField == null) {
        return result; // no need to clone null
    }

    final Field unsafeField;
    try {
        unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
    }
    catch (NoSuchFieldException e) {
        throw new AssertionError(e);
    }
    unsafeField.setAccessible(true);
    final Unsafe unsafe;
    try {
        unsafe = (Unsafe) unsafeField.get(null);
    }
    catch (IllegalAccessException e) {
        throw new SecurityException(e);
    }

    // Update final field
    try {
        unsafe.putObjectVolatile(
                result,
                unsafe.objectFieldOffset(
                        FinalClone.class.getDeclaredField("finalField")),
                finalField.clone());
    }
    catch (NoSuchFieldException e) {
        throw new AssertionError(e);
    }

    return result;
}
}

There isn't any problem in cloning final fields it works as for others but not effecitively all the time.

Sometime it becomes problem for mutable objects

When we use clone by default it gives shallow copy ( reference to same Object ) , so while overriding clone we try to deep copy all mutable objects.When we try to deep copy final fields problem occurs as final reference ( of final field ) can not be reassigned to new Object.

public class Person implements Cloneable
{
    private final Brain brain; 
    private int age;
    public Person(Brain aBrain, int theAge)
    {
        brain = aBrain; 
        age = theAge;
    }
    public Object clone()
    {
        try
        {
            Person another = (Person) super.clone();
            // shallow copy made so far. Now we will make it deep

            another.brain = (Brain) brain.clone();

//ERROR: you can't set another.brain 

            return another;
        }
        catch(CloneNotSupportedException e) {} 
        //This exception will not occur
    }

}

This example is from the same blog mentioned in another answer by Davefar

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!