1

I read Effective Java book and don't understand one paragraph where explained Clonable interface. Can someone explain me this paragraph:

...programmers assume that if they extend a class and invoke super.clone from the subclass, the returned object will be an instance of the subclass. The only way a superclass can provide this functionality is to return an object obtained by calling super.clone. If a clone method returns an object created by a constructor, it will have the wrong class.

Thanks.

WelcomeTo
  • 19,843
  • 53
  • 170
  • 286
  • As far as I am concerned you should never use clone, you are better off with copy constructors. As far as clone goes see here: http://stackoverflow.com/questions/2326758/how-to-properly-override-clone-method – mihaisimi Jul 25 '12 at 16:20
  • 1
    Also related: http://stackoverflow.com/questions/11540792/effective-java-analysis-of-the-clone-method – assylias Jul 25 '12 at 16:22
  • 1
    @mihaisimi copy constructor doesn't allow for runtime dynamic type cloning. Example, please clone `List list`, which `List` implementation's constructor are you going to use, `ArrayList`, `LinkedList`? – Steve Kuo Jul 25 '12 at 16:32
  • The `Clonable` interface is one of the uglyest and most stupid things in the whole Java language. I have no idea why they don't deprecate it. It's a real pain to use and it causes a lot more problems than it solves. Its functionality is needed sometimes, but its implementation is a disaster. – Radu Murzea Jul 25 '12 at 16:32
  • 1
    @SoboLAN `clone` works great when implemented correctly. The problem is that most people screw it up. – Steve Kuo Jul 25 '12 at 16:36
  • @SteveKuo `clone` is one of the most poorly designed features because of how easy it is to implement incorrectly. – corsiKa Jul 25 '12 at 16:38

3 Answers3

6

I should note to begin with that clone in and of itself is broken, and that a copy constructor, like Sheep(Sheep cloneMe) is a far more elegant idiom than clone, considering the Cloneable contract is very weak. You probably already know this, since you're reading the book, but it's worth putting in here.

Anyway, to answer the question:

Object.clone() will create an object of the same type as the object it was called on. For this reason, it is strongly encouraged to "cascade" up to Object for getting the result you plan to return. If someone decides to not follow this convention, you will end up with an object of the type of the class that broke the convention, which will cause a multitude of problems.

To illustrate I have a class like so

class Sheep implements Cloneable {

    Sheep(String name)...

    public Object clone() {
        return new Sheep(this.name); // bad, doesn't cascade up to Object
    }
}

class WoolySheep extends Sheep {

    public Object clone() {
        return super.clone();
    }
}

Suddenly, if I do

WoolySheep dolly = new WoolySheep("Dolly");
WoolySheep clone = (WoolySheep)(dolly.clone()); // error

I'll get an exception because what I get back from dolly.clone() is a Sheep, not a WoolySheep.

corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • @jtahlborn It assumed, as the question asked, that `Sheep.clone()` was improperly implemented with the given constructor. I have edited it in to make it explicit. – corsiKa Jul 25 '12 at 16:28
  • The important thing is that `clone`ing is unethical :-) – corsiKa Jul 25 '12 at 16:29
  • hm. thanks, but I still don't understand one think. First Bloch explain that using `super.clone` from subclass we can't get object of "subclass type". But we can achieve this calling `super.clone` on superclass. Where is logic?... – WelcomeTo Jul 25 '12 at 16:29
  • @MyTitle If we cascade all the way up to `Object.clone()`, then the result of a `clone()` call will be the same class as the object being called, because that's how `Object.clone()` does it. If instead someone decides to NOT cascade up to `Object.clone()`, and return something called with a constructor, they will get an object of a type that is between the called object and and `Object`, which is not intended. – corsiKa Jul 25 '12 at 16:33
  • @corsiKa - it wasn't assumed. the OP is confused about this mechanism. the whole _point_ of the confusion is using a constructor instead of `super.clone`. – jtahlborn Jul 25 '12 at 16:36
  • 3
    @MyTitle - you are misreading the quote in the book. using `super.clone` is the _correct_ implementation. – jtahlborn Jul 25 '12 at 16:37
  • And *correct* means *as correct as you can get using clone*. Clone is a pretty poor feature as far as Java features go. – corsiKa Jul 25 '12 at 16:54
2

I don't agree with @corsiKa's answer. since Java5.0. Java supports covariant return type therefore, the correct implementation for clone() should be:

class Sheep implements Cloneable {

    Sheep(String name)...

    public Sheep clone() {
        return new Sheep(this.name);
    }
}

class WoolySheep extends Sheep {

    public WoolySheep clone() {
        return super.clone(); // compile time error, Type miss match.
    }
}

Also the suggested alternative copy constructor does not support polymorphism. consider following example (which copy constructor can't do):

interface Animal implements Cloneable {
  String whatAreYou()
}

class Cat implements Animal {
  String whatAreYou() {
    return "I am a cat";
  }
  Cat clone() {
    return new Cat();
  }
}

class Dog implements Animal{
  String whatAreYou() {
    return "I am a dog";
  }
  Dog clone() {
    return new Dog();
  }
}

class Lib {
  Animal cloneAnimal(Animal animal) {
    return animal.clone();
  }
}
1
class A {
    protected Object clone() {
        return new A();
    }
}

class B extends A implements Cloneable {
    public Object clone() {
        return super.clone();
    }
}

Here, A has an invalid implementation of clone because this will throw an exception:

B obj = (B)(new B()).clone();

Instead, A.clone() must call super.clone() instead of a constructor. Object.clone() will then generate a new object of the run-time type instead of the compile-time type.

Any fields are then cloned onto this new object. It wpould be tempting to use a constructor if you already have one that initialises all your fields (like a copy-constructor), but that will result in incorrect behaviour for any subclasses.

If the class is final, then it does not matter, as it can't have any subclasses.

OrangeDog
  • 36,653
  • 12
  • 122
  • 207