1

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.

user1567060
  • 179
  • 1
  • 3
  • 11
  • 1
    Are you trying to solve this for *any* class that implements your kind of deep clone? Generally speaking using instanceof in this manner tends to be a code smell. What happens later when a new class appears? You should reconsider the overall design problem. http://www.javapractices.com/topic/TopicAction.do?Id=31 – avgvstvs Mar 12 '14 at 18:24

2 Answers2

1

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.
}
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • Sorry but that doesn't work. I get the following error: cannot find symbol symbol : method clone() location: interface java.lang.Cloneable b.add(c.clone()); Perhaps because clone() is in Object and not in Cloneable? (Cloneable is empty) – user1567060 Mar 12 '14 at 18:11
  • Every Cloneable is also of type Object, and the Object version of clone is also not visible. – Elliott Frisch Mar 12 '14 at 18:17
  • @user1567060 Edited to use reflection. – Elliott Frisch Mar 12 '14 at 18:24
  • I would recommend not to use reflection if you don't have to. It is really error-prone and you can find easier ways to solve this problem. – Mirco Mar 12 '14 at 18:36
  • It looks like your reflection idea worked. I considered reflection but didn't quite think it up in this way. I trust you on Cloneable extending Object (since I'm told all objects extend Object) but c.clone() still didn't work. It looks like this idea will work, though. I appreciate your help and will let you know when I implement the solution into the actual program. (Probably later today.) – user1567060 Mar 12 '14 at 18:37
  • @verbose-mode I'll keep that in mind and will study other ideas too. I just need something working fast. Thanks. – user1567060 Mar 12 '14 at 18:38
1

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 :) )

Community
  • 1
  • 1
Mirco
  • 2,940
  • 5
  • 34
  • 57