1

How to clone a Java object with the clone() method

I have a question regarding properly implementing the clone() method for a class in java. I know that this is bad practice, but I need to know this for an exam.. In the above discussion they say to call super.clone() - but I don't udnerstand what happens if the super function doesn't implement Clonable. For example, say I have a class X that extends Y. X implements Clonable and Y doesnl't. Y's clone() method should throw an Exception. Then what do we do in this case?

All the explanations I could find somehow assume that all superclasses implement Clonable, or at least that's what I understood..

EDIT:

Check out this code please:

public class Employee implements Cloneable {

private String name;

public Employee(String name) {
    this.name = name;
}

public String getName() {
    return name;
}

public Object clone()throws CloneNotSupportedException{  
    return (Employee)super.clone();  
}

public static void main(String[] args) {
    Employee emp = new Employee("Abhi");
    try {
        Employee emp2 = (Employee) emp.clone();
        System.out.println(emp2.getName());
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
}

}

It is taken from here: https://www.javacodegeeks.com/2018/03/understanding-cloneable-interface-in-java.html

Similar code can be found in many tutorials.

Why can they use super.clone() when the superclass (which in this case is Object) does not implement Clonable - that would result in an Exception.

PhysicsPrincess
  • 342
  • 1
  • 10
  • If 'superclass is not Clonable' means superclass does not implement 'Clonable' interface - there will be no super.clone() method, so you would not be able to call it. So what the question? – Alex Chernyshev Jan 25 '20 at 22:06
  • 1
    So how do I clone in this case? How do I override the clone() method? – PhysicsPrincess Jan 25 '20 at 22:07
  • Object does not implement Clonable as well.. So every class extends Object.. So we will always reach a not Clonable class using super.clone()... So why do we do that in every clone() implementation I could find? – PhysicsPrincess Jan 25 '20 at 22:08
  • 1
    @AlexChernyshev There _will_ be a `super.clone()`, because it's declared on `Object`; it just won't work if the object does implement `Clonable` – khelwood Jan 25 '20 at 22:09
  • You cannot override something that does not exist, so full clone() method implementation will be yours. In simplest case just return new instance of class with copied fields. – Alex Chernyshev Jan 25 '20 at 22:09
  • 2
    @AlexChernyshev [`clone()` is a method of `Object`](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Object.html#clone()). Thus, every object in Java does have a `clone()`-method (with more than questionable behaviour). – Turing85 Jan 25 '20 at 22:15
  • I added a code example when this thing happens. – PhysicsPrincess Jan 25 '20 at 22:16
  • @Andy That is not correct. `Object.clone()` will clone all the private fields and not throw `CloneNotSupportedException`. – user207421 Jan 25 '20 at 22:19
  • @Andy this is exactly my question - this should throw a runtime exception. So why is this used? – PhysicsPrincess Jan 25 '20 at 22:21
  • @Andy The derived class implements `Cloneable`, which turns that behaviour off. Try it before you post any more misinformation. – user207421 Jan 25 '20 at 22:22
  • @Turing85 ok thanks for noticing that, hope you also seen that 'Invoking Object's clone method on an instance that does not implement the Cloneable interface results in the exception CloneNotSupportedException being thrown.' https://docs.oracle.com/javase/7/docs/api/java/lang/Cloneable.html So is it much differs from not having such method at all ? – Alex Chernyshev Jan 25 '20 at 22:23
  • @AlexChernyshev and I hope you know that [`Cloneable` is broken](https://www.artima.com/intv/bloch.html). – Turing85 Jan 25 '20 at 22:25
  • @AlexChernyshev The OP's instance *does* implement `Cloneable`. You guys need to read more carefully. – user207421 Jan 25 '20 at 22:25
  • 1
    @Turing85 lot of people think that 'Java is broken' , so? Let's stay on topic. – Alex Chernyshev Jan 25 '20 at 22:26

2 Answers2

2

If you have this structure:

class Y {}

class X extends Y implements Cloneable {
    @Override
    public X clone() {
        try {
            return (X) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        } 
    }
}

Then clone on instances of X will work fine. It won't work on direct instances of Y, because they are not declared cloneable. But the Cloneable interface on X is an indicator to the mechanisms of the default clone() implementation that they should be made to work.

Alternatively

You could also have a non-Cloneable class with a working clone() method, as long as you didn't rely on the default implementation of clone().

For instance:

class Y {
    @Override
    public Y clone() {
        // Don't call super.clone() because it will error
        return new Y(...); // whatever parameters
    }
}

However, with this mechanism, if you called super.clone() from a subclass of Y, you would get an instance of Y, which is probably not what you would want.

As others have pointed out, the Cloneable mechanism is awkward and confusing, and usually copying mechanisms using new are easier to work with.

khelwood
  • 55,782
  • 14
  • 81
  • 108
  • 1
    Why does it work on instances of X? The clone method you've written would result in an Exception. And what do you mean by the default implementation? – PhysicsPrincess Jan 25 '20 at 22:26
  • @PhysicsPrincess Would result in an exception why? Have you tried it? Have you read the Javadoc for `Object.clone()` which is the default implementation? Too much guesswork in this question. – user207421 Jan 25 '20 at 22:28
  • 2
    The clone methods in my answer would work without an error. The default implementation of `clone()` is the one that you get from calling `super.clone()` all the way up to the `Object` class, and that works as long as the instance you're cloning implements `Clonable` – khelwood Jan 25 '20 at 22:28
  • 1
    @khelwood I read in the Java API that when a class does not implement Clonable, its clone method results in an exception. So do you mean that there is a different implementation to that? – PhysicsPrincess Jan 25 '20 at 22:31
  • @PhysicsPrincess Your class *does* implement `Cloneable`. And I think you misread what you read. – user207421 Jan 25 '20 at 22:33
  • 1
    @khelwood true that (deleted the comment already). This, however, will only work as long as `Y` itself does not override `clone()` with, e.g. `return new Y();`. – Turing85 Jan 25 '20 at 22:33
  • 1
    @PhysicsPrincess If `X` implements `Clonable`, then you can call `super.clone()` and it won't throw an exception, even if `Y` is not clonable, because **the object you're cloning** is still an instance of `X`, a clonable class. – khelwood Jan 25 '20 at 22:36
  • 1
    @Turing85 Yes, the `Cloneable` magic only works if you use `super.clone()` all the way up the inheritance hierarchy to `Object`. – khelwood Jan 25 '20 at 22:38
  • 1
    @khelwood I'm sorry if this is common knowledge but I'm struggling to understand - the object I'm cloning is indeed an instance of X. But as far as I know, the word "super" is a reference to Y's clone() method. And if Y isn't Clonable, then how come Y's clone method doesn't throw an exception? – PhysicsPrincess Jan 25 '20 at 22:42
  • @PhysicsPrincess The instance on which you are calling `super.clone()` is `Cloneable`. That's all that matters. There is no wording to the effect you suggest. Otherwise the whole mechanism would be completely useless. – user207421 Jan 25 '20 at 22:43
  • 1
    @PhysicsPrincess Because the JVM checks "is this object I'm trying to clone a member of a class that implements `Cloneable`?" If it is, then create a copy of it. If it's not, throw a `CloneNotSupportedException`. – khelwood Jan 25 '20 at 22:45
  • @PhysicsPrincess 'I read in the Java API that when a class does not implement `Cloneable`, its clone method results in an exception': no you didn't. Have a good look at what it really says. – user207421 Jan 25 '20 at 22:59
1

The Cloneable-interface is generally regarded as broken (and won't be fixed). At the core, the argument revolves around the fact that clone() is a method defined on Object, instead of being a method of the interface Cloneable.

I would not recommend using it at all. A better solution would be to provide copy-constructors. If one does not have the capability to fully recreate a parent-class object, then cloning is impossible.

Refactoring the code provided would lead to a result similar to this:

public class Employee implements Cloneable {

    private String name;

    public Employee(String name) {
        this.name = name;
    }

    public Employee(Employee that) {
        this.name = that.name;
    }

    public static void main(String[] args) {
        Employee emp = new Employee("Abhi");
        Employee emp2 = new Employee(emp);
        System.out.println(emp2.getName());
    }

    public String getName() {
        return name;
    }
}

A remark on your code:

public class Employee {
    public Object clone()throws CloneNotSupportedException{  
        return (Employee)super.clone();  
    }
}

The type cast is superfluous since the methode returns an Object.

Turing85
  • 18,217
  • 7
  • 33
  • 58
  • 2
    I know about copy constructors, but I'm asking because we are required in class to know this as well – PhysicsPrincess Jan 25 '20 at 22:17
  • 1
    Then tell your professor/supervisor/teacher that `Cloneable` is (was and will be) broken and should not be used. If s/he asks why, point her/him to the link I have provided. For reference: Josh Bloch was a software engineer at Sun Microsystems and has implemented, among other things, the `Collections` framework. – Turing85 Jan 25 '20 at 22:20
  • Of course, Java being Java where objects do not need to have an explicit owner, the trick is usually not to have to clone anything at all. Preferably classes could be made immutable so that cloning is completely superfluous (you can still have methods that return a changed object instance, by the way). – Maarten Bodewes Jan 28 '20 at 01:15