Java: super.clone() method and inheritance

19,347

Solution 1

It sounds like there are at least two problems at work here:

  1. It sounds like you're confused about how clone() normally gets implemented.

  2. It sounds like you're thinking that cloning is a good idea (vs. using a copy constructor, factories or their equivalent).

Here is an example of an implementation of a clone method:

@Override 
public Object clone() throws CloneNotSupportedException {   
    //get initial bit-by-bit copy, which handles all immutable fields
    Fruit result = (Fruit)super.clone();

    //mutable fields need to be made independent of this object, for reasons
    //similar to those for defensive copies - to prevent unwanted access to
    //this object's internal state
    result.fBestBeforeDate = new Date( this.fBestBeforeDate.getTime() );

    return result;
}

Note that the result of super.clone() is immediately cast to a Fruit. That allows the inheriting method to then modify the Fruit-specific member data (fBestBeforeDate in this case).

Thus, the call to a child clone() method, while it will call the parents' clones, also adds its own specific modifications to the newly made copy. What comes out, in this case, will be a Fruit, not an Object.

Now, more importantly, cloning is a bad idea. Copy constructors and factories provide much more intuitive and easily maintained alternatives. Try reading the header on the Java Practices link that I attached to the example: that summarizes some of the problems. Josh Bloch also has a much longer discussion: cloning should definitely be avoided. Here is an excellent summary paragraph on why he thinks cloning is a problem:

Object's clone method is very tricky. It's based on field copies, and it's "extra-linguistic." It creates an object without calling a constructor. There are no guarantees that it preserves the invariants established by the constructors. There have been lots of bugs over the years, both in and outside Sun, stemming from the fact that if you just call super.clone repeatedly up the chain until you have cloned an object, you have a shallow copy of the object. The clone generally shares state with the object being cloned. If that state is mutable, you don't have two independent objects. If you modify one, the other changes as well. And all of a sudden, you get random behavior.

Solution 2

Its a special native method. This was done to make cloning easier. Otherwise you will have to copy the whole code of your ancestor classes.

Solution 3

Although one answer is accepted, I do not think it fully answers the first part of the question (why downcasting in subclasses always works). Although I cannot really explain it, I think I can clear up some of the poster's confusion which was the same as mine. We have the following classes

class A implements Cloneable 
{
   @Override
   protected A clone() throws CloneNotSupportedException // could be public
   { 
      Object clone = super.clone();
      System.out.println("Class A: " + clone.getClass()); // will print 'C'
      return (A) clone;
   }
}

class B extends A
{
   @Override
   protected B clone() throws CloneNotSupportedException
   { 
      A clone = super.clone();
      System.out.println("Class B: " + clone.getClass()); // will print 'C'
      return (B) clone;
   }
}

class C extends B
{
   @Override
   protected C clone() throws CloneNotSupportedException
   { 
      B clone = super.clone();
      System.out.println("Class C: " + clone.getClass()); // will print 'C'
      return (C) clone;
   }
}

static main(char[] argv)
{
   C c = new C();
   C cloned_c = c.clone();
}

The result of this is that

Class A: C

Class B: C

Class C: C

is printed on the command line. So, as a matter of fact, the clone()method of Object somehow can look down the call stack and see which type of object at the start of the chain invoked clone(), then, provided the calls bubble up so that Object#clone()is actually called, an object of that type is created. So this happens already in class C, which is strange, but it explains why the downcasts do not result in a ClassCastException. I've checked with the OpenJDK, and it appears this comes by some Java black magic implemented in native code.

Solution 4

If clone() in B returns whatever clone() in A returns and clone() in C returns whatever the clone() in B returns then clone() in C will return whatever the clone() in A returns.

Share:
19,347
Shuzheng
Author by

Shuzheng

Updated on June 03, 2022

Comments

  • Shuzheng
    Shuzheng about 2 years

    I have a quick question regarding the clone() method in Java, used as super.clone() in regard to inheritance - where I call the clone() method in the parent class all the way up from the button.

    The clone() method is supposed to return a copy of this object, however if I have three classes in an inheritance heirachy and call super.clone() three times, why doesn't the highest class in the inheritance heirachy, just under class Object, get a copy of that class returned?

    Suppose we have three classes: A, B and C, where A -> B -> C (inherit = ->)

    Then calling super.clone() in class C, invokes clone() in B which calls super.clone(), invoke clone() in A which call super.clone() 'this time Object.clone() gets called'. Why is it not a copy of the this object with respect to class A that gets returned from Object.clone()? That sounds logical to me.

  • Shuzheng
    Shuzheng almost 12 years
    Thanks man, the book i'm reading - Absolute Java with Walter Savitch actually implements clone and copy constructor similar - they both call some copyOf method (private), that return a reference to the the clone. Will take a look on your papers :)
  • Shuzheng
    Shuzheng almost 12 years
    My point was more, since class A call super.clone() to invoke the Object.clone(), why dont only the part of this object that belong to class A get copied, such that I have a crippled object only with properties on class A. When I call super.clone() from class C, does a this pointer get transfered to all the way up the hiearchy until super.clone @ class A, such that Object.clone knows "this" object I must clone ??
  • Steve Kuo
    Steve Kuo almost 12 years
    -1 for "cloning is a bad idea." There are times when clone is warranted and copy constructor won't work. Consider cloning an object and not knowing the exact type at runtime. In that case which class's copy constructor do you use? clone is a bit tricky to get right, but when done properly, it works great. See my answer to another clone question on how to implement clone properly stackoverflow.com/questions/1052340/…
  • Shuzheng
    Shuzheng almost 12 years
    My point was more, since class A call super.clone() to invoke the Object.clone(), why dont only the part of this object that belong to class A get copied, such that I have a crippled object only with properties on class A. When I call super.clone() from class C, does a this pointer get transfered to all the way up the hiearchy until super.clone @ class A, such that Object.clone knows "this" object I must clone ??
  • Bob Cross
    Bob Cross almost 12 years
    @SteveKuo, you may have missed the rest of the phrase where I suggested "copy constructors and factories." It's quite easy to make polymorphic factories. One of my primary fears when it comes to cloning is that I'm hoping that everyone other class in the heirarchy implemented clone properly (e.g., no short-cut shallow copies).
  • Bob Cross
    Bob Cross almost 12 years
    @NicolasLykkeIversen, check the example again: first there's the call to super.clone() that's cast to a child class. Then you add data to that child class. Then you return the child.
  • Shuzheng
    Shuzheng almost 12 years
    Yes i see (: - My new question is: does Object.clone() clone all of "this" object, or just the part of "this" that belongs to class A, since super.clone() is called from class A in the last step towards Object.clone() ?? If not, do a "this" pointer gets traveled all the way up to super.clone() in class A, such that Object.clone clone all of the calling object "this object" ?? I've read much about how you should implement clone, and let each part of the class hierachy, replace the part of the object being cloned, with copies of mutable object instance variables. Thanks alot in advance.
  • Bob Cross
    Bob Cross almost 12 years
    @NicolasLykkeIversen, trying to summarize: Parent.clone() only clones the Parent's portion. Then you have to add the Child's personal data in the Child.clone() implementation.
  • Bob Cross
    Bob Cross about 11 years
    @SteveKuo, revisiting this answer almost a year later, I still agree with Josh Bloch. Cloning is still a bad idea.
  • oarfish
    oarfish about 10 years
    I don't understand how this answers the first part of the question. The only class actually calling Object#clone() is A. Thus, Object#clone() could look what type of class it is invoked on and create such an instance -- an instance of class A, here. So within A#clone() the downcast makes sense. But now in class B inheriting from A, if in B#clone() we call super.clone() (=A#clone()), what this will return is an object of type A. We then downcast it to B which should result in a ClassCastException. I don't understand how it is ensured that what we have is of type B.
  • Bob Cross
    Bob Cross about 10 years
    @oarfish, your comment is long enough that it's effectively its own question. Perhaps you should ask that question (and refer back here if you like) so it can be considered separately.
  • oarfish
    oarfish about 10 years
    @BobCross: Well I think it is the same question, which is why I am commenting it here. It is my impression that the poster's confusion is related to exactly this issue.
  • sudeepdino008
    sudeepdino008 about 8 years
    The return type of the above function could be Fruit. Without it the calling method will need to typecast it to Fruit.