If you don't clone in Java then what do you do

2019-04-20 02:27发布

问题:

Does anyone have any suggested or established best practices and naming conventions for copy constructors / factory methods etc in Java? In particular, say I have a class Thing and I want a method somewhere that returns a new Thing with the same value as a Thing passed in (or as the instance if it's an instance method). Would you have this as constructor or a static factory method or instance method? What would you call it?

As per the title, I want to avoid clone() and Cloneable.

回答1:

Effective Java recommends either of the following:

  1. A copy constructor (as noted by others):

    public Item(Item item)

  2. A copy factory method:

    public static Item newInstance(Item item)

(Also, no copying for immutables)

The primary difference is that with #1 you choose the actual class of the result, and with #2 the implementer can return a subclass. The semantics of the class may guide you into which one is best.



回答2:

I would call it a copy method or a copy constructor (as the case may be). If it was a static method, then I would call it a factory.

In terms of what to do, the most flexible and long living option is a copy constructor. This gives subclasses the ability to copy themselves just like the parent.



回答3:

I'd do a constructor

...
public Thing(Thing copyFrom)
{
    attr1 = copyFrom.attr1;
    attr2 = copyFrom.attr2;
    //etc...
}

then when you want to clone it

Thing copy = new Thing(copy from me);


回答4:

You can overwrite the clone()-method, if you want. Another used practice is a constructor, that takes an object of this type, i.e. new ArrayList(anotherList).



回答5:

You've got a few options, implement Cloneable, add a copy constructor but my preferred way is to use a method (static or instance) that has a name which is descriptive of what the copy operation is doing - is it a deep or shallow copy, etc.



回答6:

Use immutable data structures. The only reason you feel that you need clone() is that you're mutating your objects in place. Stop doing that. Think about how you can:

  • Make your classes final.
  • Make fields in your classes final and private.

For example, here's a "setter" for an immutable 3D vector object:

public Vector3D setX(double x) {
  return new Vector3D(x, this.y, this.z);
}

So I guess what I'm saying is... I use copy constructors instead of mutation, and I just name them according to the attribute that I want to modify.



回答7:

Another option is to implement the copying method in the source object, e.g.:

interface Has3DCoords {
    void setLocation(double x, double y, double z);

    void copyCoordsTo(Has3DCoords dest);
}

You would then implement copying with a piece of code like:

class Thing implements Has3DCoords {
    private Point3D loc;
    // ...

    void setLocation(double x, double y, double z) {
        loc.setLocation(x, y, z);
        // or: loc = new Point3D(x, y, z);
    }

    void copyCoordsTo(Has3DCoords dest) {
        loc.copyCoordsTo(dest);
        // or: dest.setLocation(loc.getX(), loc.getY(), loc.getZ());
    }

    OtherThing createOtherThing() {
        OtherThing result = new OtherThing();
        this.copyCoordsTo(result);
        return result;
    }
}

This can be useful if:

  • It does not make sense to clone the whole object
  • There is a group of related properties that are often copied as one unit
  • You do not want to expose loc as a property of Thing
  • The number of properties is large (or there are many such groups) so a constructor that required all of them as parameters would be unwieldy.


回答8:

This is not the nicest approach to copying objects but the following is sometimes useful if you wish to perform a deep copy of a Serializable object. This avoids having to write copy constuctors, implement Cloneable or writing factory classes.

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);

//Serializes the input object
oos.writeObject(input);

ByteArrayInputStream bais = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);

//Copy of the input object
Object output = ois.readObject();

Don't forget to handle the exceptions and to close the streams nicely.



标签: java oop