user1567060
user1567060

Reputation: 179

Java Deep Clone - Clone each element in ArrayList if Cloneable, regardless of type

I have an ArrayList a, which can contain objects of any type. I then need to loop through the elements of the ArrayList. If the element implements Cloneable, I need to clone the element (meaning make a copy of it and give it its own place in memory) and add the cloned element to ArrayList b. The problem is with casting. I need to cast to the right object type and then call clone(). (Because clone() in Object is protected.)

In the code below, I have managed to do that with a bunch of if statements. On each element, I say if Cloneable, and then nested in that if statement I have more if statements, checking for instance of the right type. When the program finds the right type, it casts and clones. All of this code works, but I would like a way to cast and then clone() without these if statements, since there could be classes I don't know of yet. Is there a way to do this? (I can cast using Class.cast() but I haven't had success calling clone() on such casts.)

I have tried to serialize the array list, implement my own interface (with a clone() method), and call a copy constructor. None of those work for every case. Near as I can tell, casting and then cloning is the only sure way to make it work. If there's a way to cast and clone without specifying the exact class, that will work for every case. Otherwise, I'm stumped and need another solution. (If there's a way to loop through every field in an object regardless of accessibility, that might work too.)

Here's my code:

// Establish variables
TestClass t = new TestClass();
TestClass u = new TestClass(1);
Point p = new Point();
Point q = new Point(1, 2);

// Set up ArrayList a
ArrayList a = new ArrayList();
a.add(t);
a.add(u);
a.add(p);
a.add(q);

// Get number of elements in a
int n = a.size();

// Set up ArrayList b
ArrayList b = new ArrayList();

// Clone all Cloneable elements from a into b
for (int i = 0; i < n; i++)
    if (a.get(i) instanceof Cloneable)
        if (a.get(i) instanceof TestClass)
            b.add(((TestClass)a.get(i)).clone());
        else if (a.get(i) instanceof Point)
            b.add(((Point)a.get(i)).clone());
    //ISSUE: I need to be able to cast and then clone based on the class a.get(i) represents, to avoid these if statements.

// Change initial variables. They change in ArrayList a (due to referencing the same memory) but not b
t.i = 2;
u.i = 3;
p.x = 3;
p.y = 4;
q.x = 5;
q.y = 6;

System.out.println(a);
System.out.println(b);

TestClass and Point are classes that I have written. They implement Cloneable.

Thank you for any help that is provided.

Upvotes: 1

Views: 402

Answers (2)

Mirco
Mirco

Reputation: 3016

The whole clone mechanism in Java is basically broken. See for example http://www.artima.com/intv/issues3.html.

You could use a library which has all the functions in place (http://code.google.com/p/cloning/ - (Deep clone utility recomendation credits to Cojones ) or create a own interface like

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

Let all classes you need implement that interface. If you know which class it is, you can just normally use it like

SomeClass a = new SomeClass();
SomeClass clone = a.copy();

Or if you only know that it is a Copyable:

Copyable<?> copy = x.copy();

But in most cases, it is better to use an existing solution like the libary above.

(Edit: It is always the best choice not to use javas own clone stuff :) )

Upvotes: 1

Elliott Frisch
Elliott Frisch

Reputation: 201439

I think you can do this using reflection,

if (a.get(i) instanceof Cloneable) {
  Cloneable c = (Cloneable) a.get(i);
  try {
    Method cloneMethod = c.getClass().getMethod("clone", new Class<?>[] {});
    cloneMethod.setAccessible(true);
    Object clone = cloneMethod.invoke(c, new Object[] {});
    b.add(clone);
  } catch (Exception e) {
    // Add real error handling.
    e.printStackTrace();
  }
} else {
  // It isn't cloneable.
}

Upvotes: 1

Related Questions