views:

75

answers:

4

What is a quick and easy way to fill a Java array with clones of a single object?

e.g. after:

Rectangle[] rectangles = new Rectangle[N];
fillWithClones(rectangles, new Rectangle(1, 2, 3, 4));

the rectangles array would contain N distinct Rectangle instances, initialised with the same coordinates.

I am aware of the flaws of Object.clone() in Java, but in this case I know that the objects to be copied have non-throwing, public clone() methods but may or may not have a public copy constructor.

I'm guessing there is a library method somewhere that does this, but I don't think it's in the JDK, Commons-collections or Guava.

A: 

For rectangles:

public void fillWithClones(Rectangle[] arr, Rectangle src) {
    for(int xa=0,len=arr.length; xa<len; xa++) { arr[xa]=(Rectangle)src.clone(); }
    }
Software Monkey
For types unknown, `Object.clone` is protected. You can only access that method via reflection (and only if the target type defines a public override, of course).
Chris Jester-Young
@Chris: Crap, what a screw-up Clonable is. I deleted the incorrect untyped method.
Software Monkey
+2  A: 

If you don't have the specific type you want to work with at compile time, you will have to invoke the clone method by reflection.

private static <T> T cloneByReflection(T object) {
    try {
        return (T) object.getClass().getMethod("clone").invoke(object);
    } catch (Exception e) {
        return null;    // or whatever you want to do
    }
}

public static <T> void fillWithClones(T[] array, T template) {
    for (int i = 0; i < array.length; ++i)
        array[i] = cloneByReflection(template);
}
Chris Jester-Young
A: 

@Chris Jester-Young answer gives you a recipe for doing what you want to do.

But I'd suggest that there is something rather wrong about an application that does this sort of thing.

  • Why does your application need to make deep copies of arrays of random things? If you don't know the things' types, how do you know that copying is necessary?

  • What is your application supposed to do when the array contains a non-clonable object? Do you throw an exception? Do you put a null in the array, requiring later null checking?

It is better design to have all of the objects that you may want to clone implement an interface with a (public) clone method method. That way you can have a statically typed solution (no dynamic type exceptions!) and you can avoid the overheads of invoking clone reflectively.

Stephen C
The types are not "random". The type is known in each case, but I would prefer a generic method to keep the program smaller. Some of the classes are from the JDK so I cannot define a new interface for them. If this method encounters a non-cloneable object it should throw a `CloneNotSupportedException`.
finnw
@finnw - what about the first bullet? Why does your application need to clone objects in the first place?
Stephen C
A: 

If a copy constructor might exist (and you want to use it if it does), you could do something like this:

(Edit: Updated code to use an array instead of List):

private static <T> void fillWithClones( T[] array, T object )
{
  try
  {
    @SuppressWarnings("unchecked")
    Class<T> clazz = (Class<T>)object.getClass();
    Constructor<T> c = clazz.getConstructor( object.getClass() );

    try
    {
      for ( int i = 0; i < array.length; i++ )
      {
        array[i] = (T)c.newInstance( object );
      }
    }
    catch ( Exception e )
    {
      // Handle exception or rethrow...
    }
  }
  catch ( NoSuchMethodException e )
  {
    // No copy constructor, try clone option...
  }
}

Some of the exception handling could be tidied up, of course.

Ash
A constructor `T(T)` is not necessarily a copy constructor (e.g. `JFrame`, `JScrollPane`, `ClassLoader`, `Properties`, `Throwable`.)
finnw
@finnw: Yeah, good pickup. I guess at the end of the day you need to know _something_ about the objects you're duplicating to choose an appropriate solution.
Ash