Question

(Title sounds a bit too fancy but I couldn't find a match so we'll hope it's descriptive.)

I have a working piece of code as follows:

@SuppressWarnings("unchecked")
public static Copyable[] copyOf(Copyable[] objects, Class type) {
    Copyable[] copies = Arrays.copyOf(objects, objects.length, type);
    for( int i=0; i < copies.length; ++i ) {
        if( objects[i] == null )
            throw new IllegalArgumentException("Copyable[] contains null reference", new NullPointerException());
        copies[i] = objects[i].copy();
    }

    return copies;
}

Not as pretty as I would like, since I have to pass in the array class, but it works for what I want it to do: allocate a new array of a Copyable implementor and fill it using the implemented method.

The problem I'm having is that this gets compiled by GWT, whose JRE Emulation library barfs on correct Java that it hasn't implemented. I need to do this without calling Arrays.copyOf(), and hopefully without reflection.

Note: I'm working on a clone() version but don't really want to rely on clone() either. I feel like there must be a cleaner solution.

Was it helpful?

Solution

How about this:

public static Copyable[] copyOf(Copyable[] objects) {
    Copyable[] copies = new Copyable[objects.length];
    for( int i=0; i < copies.length; ++i ) {
        if( objects[i] == null )
            throw new IllegalArgumentException("Copyable[] contains null reference", new NullPointerException());
        copies[i] = objects[i].copy();
    }
    return copies;
}

There's no need to actually copy the objects array into copies before making copies of the elements themselves.

EDIT

If your Copyable objects are serializable, you can just serialize and then deserialize the array to create a copy. There's a utility function DeepCopy.java in gwt-test-utils that may do exactly what you need.

OTHER TIPS

Why can't you just do: Copyable[] copies = new Copyable[objects.length]; ? That line allocates the array and then you're already filling it with the for loop you included.

Perhaps with Generics:

public interface Copyable<T> {
  T copy();
}



import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

public class Test {
  static class My implements Copyable<My> {
    int x;

    public My copy() {
      My my = new My();
      my.x = this.x;
      return my;
    }
  }

  @SuppressWarnings("unchecked")
  public static <T extends Copyable<T>> T[] copyOf(T[] objects, Class<T> type) {
    List<T> copies = new ArrayList<T>();
    for (int i = 0; i < objects.length; ++i) {
      if (objects[i] == null) throw new IllegalArgumentException("Copyable[] contains null reference",
          new NullPointerException());
      copies.add((T) objects[i].copy());
    }
    T typeVar = null;
    try {
      typeVar = (T) Class.forName(type.getName()).newInstance();
    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
    Object t = Array.newInstance(typeVar.getClass(),0);
    return copies.toArray((T[])t); 
  }

  public static void main(String[] args) {
    My[] stuff = new My[1];
    My elem = new My();
    elem.x = 1;
    stuff[0] = elem;
    My[] copies = copyOf(stuff, My.class);
    System.out.println(copies[0].x);
  }
}

Building on GriffeyDog's answer and some fun I had with generics recently, here is a solution without Class objects that works directly on arrays:

@SuppressWarnings("unchecked")
public static <T extends Copyable> T[] copyOf(T[] objects) {
    T[] copies = (T[]) new Object[objects.length];
    for (int i = 0; i < copies.length; ++i) {
        if (objects[i] == null)
            throw new IllegalArgumentException(/*...*/);
        copies[i] = (T) objects[i].copy();
    }
    return copies;
}

You can rely on clone() for the array itself, then use your copy() for each object:

@SuppressWarnings("unchecked")
public static Copyable[] copyOf(Copyable[] objects) {
    Copyable[] copies = (Copyable[]) objects.clone();
    for( int i=0; i < copies.length; ++i ) {
        if( objects[i] == null )
            throw etc;
        copies[i] = objects[i].copy();
    }
    return copies;
}

Every array implements the clone() method that returns a shallow copy of the array. So I think it's clean enough for your needs.

Notice that this way, you also don't need the argument Class type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top