WelcomeTo
WelcomeTo

Reputation: 20591

Effective Java. Clonable interface

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.

Upvotes: 1

Views: 2379

Answers (3)

astrovisitor
astrovisitor

Reputation: 21

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();
  }
}

Upvotes: 2

corsiKa
corsiKa

Reputation: 82599

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.

Upvotes: 6

OrangeDog
OrangeDog

Reputation: 38835

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.

Upvotes: 1

Related Questions