Are there any alternatives to implementing Clone i

2020-03-19 06:24发布

问题:

In my Java project, I have a vector of various types of Traders. These different types of traders are subclasses of the Trader class. Right now, I have a method that takes a Trader as an argument and stores it 50 or so times in the vector. I am having problems because storing the same object 50 times is just storing 50 references of the same object. I need to store 50 copies of the object. I've researched about implementing Clone, but I don't want the programmers defining a type of Trader to have to worry about making their class cloneable. Also, as pointed out by this page, implementing clone creates all sorts of problems. I don't think a copy constructor would work either because if I defined one in the Trader class, it would not know the type of Trader it was copying and just make a generic Trader. What can I do?

Edit: I am not really wanting to make exact copies of a certain object. What I am really trying to do is to add a certain number of Traders to the vector. The problem is that the user needs to specify in an argument which type of Trader he wants to add. Here is an example of what I am trying to do: (although my syntax is completely imaginary)

public void addTraders(*traderType*)
{
    tradervect.add(new *traderType*())
}

How can I achieve something like this in Java?

回答1:

Just add an abstract copy method. You can use covariant return types so that the derived type is specified to return a derived instance, which may or may not be important.

public interface Trader {
    Trader copyTrader();
    ...
}


public final class MyTrader implements Trader {
    MyTrader copyTrader() {
        return new MyTrader(this);
    }
    ...
}

Sometimes you might want to generically deal with a collection of derived type of Trader that needs to clone and then return the a properly typed collection. For that you can use generics in an idiomatic way:

public interface Trader<THIS extends Trader> {
    THIS copyTrader();
    ...
}


public final class MyTrader implements Trader<MyTrader> {
    public MyTrader copyTrader() {
        return new MyTrader(this);
    }
    ...
}


回答2:

I am a little unclear as to why you would want to store 50 or so clones of the same object unless they the original trader serves as a prototype (see pattern) for later traders.

If you want to make an exact copy of an object, you have to take into account the issue of polymorphism. If people subclassing your given class are allowed to add state members, then you already have enough of a headache with functions like equals and compareTo, that clone is jut one more case where you need to require special handling.

I disagree that clone is always evil, it is sometimes necessary. However, in situations of subclassing, a lot of things become tricky.

I would recommend you read (when you get a chance) Bloch's "Effective Java", which covers a lot of his topics. Bracha's view is that it is not a good idea to let others extend your classes, and that if you do, you need to document very well what they would have to do and hope that they follow your instructions. There really isn't a way to bypass that. You may also want to make your Traders immutable.

Go ahead an implement Clone normally, and indicate very clearly in the class header that anybody inheriting from your Trader and adding state members must implement X methods (specify which).

Finding tricks to bypass an actual Cloneable and still Clone isn't going to overcome the problem of inheritance. There is no silver bullet here.



回答3:

Uri's right, polymorphism with state opens up a big can of worms.

I think subclassing Cloneable and overrideing clone() is probably the simplest way to go. You can, I believe, make the return type covariant.



回答4:

One option: If you can make the objects serializable, you can serialize then deserialize it to make a copy, similar to what happens when passing an object over RMI.

Quick copy method:

public MyObject copy() {
    ObjectOutputStream oos = null;
    ObjectInputStream ois = null;
    try {
        ByteArrayOutputStream bos =  new ByteArrayOutputStream();
        oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        oos.flush();
        ByteArrayInputStream bin =
            new ByteArrayInputStream(bos.toByteArray());
        ois = new ObjectInputStream(bin);
        return (MyObject)ois.readObject();
    } catch(Exception e) {
        return null;
    } finally {
        try {
            oos.close();
            ois.close();
        } catch (Exception e) {
            return null;
        }
    }
}


回答5:

One way is to make it a final class like Java's own String, which will make any change to an object of class Trader to create a new copy in memory, but it will make it impossible to subclass it.

Another (better) way is to use a factory method to create and copy Trader objexts, which implies that you must not allow for the default constructor to be used i.e. make it a private. This way you can control the number of instances the class has. See http://en.wikipedia.org/wiki/Factory_method

public class Trader {

    /* prevent constructor so new cant be used outside the factory method */
    private Trader() {
    }

    /* the factory method */
    public static Trader createTrader(int whatKindOfTrader) {

        switch (whatKindOfTrader) {
        case 0:
            return new Trader1(); // extends Trader
        case 1:
        default:
            return new Trader2(); // extends Trader
        }
        return new Trader3(); // or throw exception
    }
}

You might even specify another overloaded method, or a second argument that takes one Trader and copies it into a new one, thus replacing clone. Btw, you might want to override the clone() method and throw CloneNotSupportedException, to prevent default Object cloning.