3

I have a class ("Manager") that manages a collection of Objects all rooted in a common superclass ("Managed"). The manager class at times needs to make copies of selected managed objects, but there is no way of knowing which subclass of Managed it is. It seems to me that the best (if not only?) way of doing this is using Cloneable. Then, for any Managed object that I need to copy, I call managedObject.clone(). Of course it must be properly implemented. I've read many admonishments to "just use a copy constructor" or implement myManagedSubClass.copy() method for all subclasses. I don't see how to use a "real" copy constructor, since I need to know the type:

ManagedSubclass copiedObject = new ManagedSubclass(existingManagedSubclassObject);

If I implement the copy() method, I think that would look like this:

class Managed {
  public Managed copy() {
    Managed newObject = new Managed(Managed other);
    // fixup mutable fields in newObject
  }
}

But in my usage I'll have to cast the return value to the expected type. If I've forgotten to implement copy() on all Managed subclasses, then I'll end up with a superclass cast to a subclass type. I can't make copy protected visibility on Managed because that is a valid class for direct copying. Even if not the case, I'd have to implement copy on every subclass that can be copied, with all the machinery to handle deep copies of mutable fields, or establish my own protocol of a protected method of some common name taking care of all the mutable fields introduced by that level of superclass.

It seems that despite the general anger and hatred for Cloneable, it is the best way to do what I want. Am I missing something?

Vasily Liaskovsky
  • 2,248
  • 1
  • 17
  • 32
ags
  • 719
  • 7
  • 23
  • Whether you use `copy()` or `clone()` it has to be a method which is defined and public for the interface you are using. After that you are dependent on how well this method was implemented no matter what. – Peter Lawrey Nov 17 '15 at 00:32
  • 1
    Perhaps you could define your own interface, `Copyable`, and require managed objects to implement it with a suitable `copy()` method. – David Conrad Nov 17 '15 at 01:02

3 Answers3

2

The right tool at the right moment.

If you need Cloneable, use it. But do it knowing all the flows it has.

clone() has a bad reputation because it's too complex for what it does and does it badly. Unless you have final fields or a zero-arg constructor calling another constructor, you should be fine using it, as long as you implement it as suggested.

Olivier Grégoire
  • 33,839
  • 23
  • 96
  • 137
  • +1 for pointing out both limitations of final fields and zero-argument constructors (though I have to think about that for a while). – ags Nov 17 '15 at 01:53
2

I prefer to use copy constructors for copying mutable objects. When writing a constructor you are forced to invoke super(...), and here you can use the copy constructor of the super class. This approach of invoking the constructor of the super class and then assigning the fields of the current class is analogous to the way you write clone methods (invoking super.clone() then reassigning fields if necessary). One advantage this has has over clone is that you never have to use a useless try {...} catch (CloneNotSupportedException e) {} construction. Another advantage that copy constructors have over using clone is that you can make mutable fields final, whereas clone requires you to reassign the field after calling super.clone() with a copy of the original.

You cannot use inheritance when writing a copy method because super.copy() returns an instance of the super class. However, if you like the idea of using a method rather than a constructor you could provide a copy method in addition to copy constructors.

Here is an example of this.

interface Copyable {
    Copyable copy();
}

class ImplA implements Copyable {

    private String field;

    public ImplA(ImplA implA) {
        this.field = implA.field;
    }

    @Override
    public ImplA copy() {
        return new ImplA(this);
    }

    // other constructors and methods that mutate state.
}

class ImplB extends ImplA {

    private int value;
    private final List<String> list;  // This field could not be final if we used clone.

    public ImplB(ImplB implB) {
        super(implB);                 // Here we invoke the copy constructor of the super class.
        this.value = implB.value;
        this.list = new ArrayList<>(implB.list);
    }

    @Override
    public final ImplB copy() {
        return new ImplB(this);
    }

    // other constructors and methods that mutate state.
}
Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • +1 for pointing out final field limitation. However, I'm still thinking the "preferred" method of using copy constructors won't work for me. I need to be able to copy objects of a generic type. ForListOfManaged { void doSomething(T managedSubclass) ... } how could I make a copy of the argument to doSomething() in that method body using copy constructors? – ags Nov 17 '15 at 01:52
  • @ags You are right you can't invoke a copy constructor on a generic `T`, but if `Managed` has a `copy` method you can do `(T) t.copy()`. I just think it's nicer to write `copy` using copy constructors (as above), rather than using clone. – Paul Boddington Nov 17 '15 at 01:55
0

Clone's power comes from its runtime dynamic behavior that works with inheritance, which a copy constructor is not suitable for.

The standard way to make an object cloneable is:

  1. Implement Cloneable
  2. Override clone() and make it public
  3. Within clone(), call super.clone() Managed obj = (Managed) super.clone();
  4. Object's clone will do a simple memory copy of all fields. This is fine for primitives and references to immutable objects. For mutable objects you'll need to clone/copy those as necessary

If Managed is ever inherited, and the subclass properly implements clone, then cloning it will return the proper type. e.g.

Managed m = new SubTypeOfManaged();
m.clone(); // returns a cloned SubTypeOfManaged
Steve Kuo
  • 61,876
  • 75
  • 195
  • 257