views:

687

answers:

5

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?

A: 

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;
        }
    }
}
Chris Thornhill
Why the downvote? It is an adequate way of comying a complex object. The resource management is completely and utterly broken though.
Tom Hawtin - tackline
Is that the only solution? I can't seem to bring myself to believe that such a simple operation would cause so much fuss. What if I would implement Clone to allow copying. Would that even be possible?
Cory Walker
@Tom: Not sure, but using serializing/deserializing for cloning is about as subtle as using memcpy instead of copy constructors in C++.
Uri
I certaintly agree it's not pretty, but it's the only solution that meets the requirement of not requiring the developers of subclasses to implement a clone()/copy() method. He was asking for alternatives, not best practices.
Chris Thornhill
This is definitely ugly, but it might be the only solution - I'm not ruling it out yet.
Cory Walker
Object.clone is the very literal memcpy of Java. Don't use it.
Tom Hawtin - tackline
Term ugly is very subjective. I personally (like a lot of other programmers) find nothing ugly about this method of cloning. See Apache Commons SerializationUtils.clone method - it's both a time saver and bug saver for many people.
grigory
Let's not forget that RMI copies objects between JVMs using serialization and deserialization, so it is hardly an obscure, unproven or unreliable way to make a copy of an object.
Chris Thornhill
+2  A: 

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);
    }
    ...
}
Tom Hawtin - tackline
I don't see how I can make the generic Trader class an interface because the Trader class has methods that need to be accessible. Making an interface would mean all those low level methods would not allow those methods to be defined in the Trader class.
Cory Walker
Whether Trader is an abstract class or interface is irrelevant. Generally an interface is preferred over an abstract class. Also prefer delegation to inheritance.
Tom Hawtin - tackline
A: 

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.

Azder
You mean immutable rather than just a final class. Immutability is probably going a bit far for a "trader" object.
Tom Hawtin - tackline
I didnt immutable, but final. There IS a difference, and personally I would never use it.But, the Factory method is a best practice one, and it is a common design patternBtw, immutable object is just not usefull i this case.
Azder
+2  A: 

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.

Uri
Your answer got me thinking. See the edit I made for more info. :)
Cory Walker
Wait, are you essentially saying that if X extends Trader, and somebody passed an instance of X to your function, you want to get another new X?
Uri
If you want this behavior, you use the (horrible) getClass.newInstance idiom:traderType.getClass().newInstance()You have to make sure that there is always a public constructor, and you probably want some init method you can always call to bring it into a legitimate state.
Uri
A: 

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.

dougfelt