Sign in to follow this  
Zbyl

[java] Cloning lists

Recommended Posts

Zbyl    133
I know this is a rather stupid question, but can't find and google solution by myself: I want to make a list of cloned elements from another list. The following code doesn't work, obviously: List<T> list; List<T> clones = new ArrayList<T>; for(T t : list) clones.add((T)t.clone()); neither does this (also obviously): List<T> list; List<T> clones = new ArrayList<T>; for(T t : list) clones.add((T)((Cloneable)t).clone()); Any suggestions?

Share this post


Link to post
Share on other sites
BitMaster    8651
Please specify why code "does not work". Does it not compile? Does it throw an exception at runtime? Unexpected results? An accurate error description like the pasted compiler error or the exception message?

Your first code sample should work except for the fact that your new list is not necessairily of the same type as the source list (use either clone() and remove the elements because they are not deep-copied by default or use reflection to discover the class of the source list).
Looking at your second code fragment, I get the suspicion that your list elements simply cannot clone themselves. In this case, modify their class accordingly.

Share this post


Link to post
Share on other sites
Zbyl    133
The code does not work, because the method clone() is not in the Clonable interface, and Object.clone() is protected.
I need code that would copy the list of T's (for any T that implements Clonable interface and has a public clone() method).

I thought I could get the object's class, ask it (via reflection) for a "clone" method, and invoke it. But is it the "right" way to do it?

Share this post


Link to post
Share on other sites
RayNbow    191
Quote:
Original post by Zbyl
The code does not work, because the method clone() is not in the Clonable interface, and Object.clone() is protected.
I need code that would copy the list of T's (for any T that implements Clonable interface and has a public clone() method).

What about...

[source=java]
interface TrulyCloneable {
public Object clone() throws CloneNotSupportedException;
}

class Foo implements Cloneable, TrulyCloneable {
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

//--------------------------------------------------

public class CopyExample {

public static void main(String[] args) {

List<TrulyCloneable> list = new ArrayList<TrulyCloneable>();
List<TrulyCloneable> clones = new ArrayList<TrulyCloneable>();

list.add(new Foo());
list.add(new Foo());
list.add(new Foo());

for(TrulyCloneable t : list)
try {
clones.add((TrulyCloneable)t.clone());
}
catch (CloneNotSupportedException e) {
e.printStackTrace();
}

for(int i = 0; i < list.size(); i++)
System.out.println(list.get(i) == clones.get(i));

}

}


Share this post


Link to post
Share on other sites
Zbyl    133
This:
List<T extends Cloneable> list;
won't work, cause, like I said, Clonable interface is empty.

'java.util.Collections' does not have any methods for cloning.

The 'TrulyCloneable' solution is not good enough either, cause I want the clone_list to work with any class implementing public clone() method.
In C++ with templates it would be very easy, and I hoped that in Java with generics it would be doable too...

So the only solution that works uses reflection:
[source=java]
public static <T> List<T> clone_list(List<? extends T> list) throws Exception
{
ArrayList<T> out = new ArrayList<T>();
for(T t : list)
{
Method m = t.getClass().getMethod("clone");
out.add((T)m.invoke(t)); //WARNING:
//Type safety: The cast from Object to T is actually checking
//against the erased type Object
}
return out;
}


But can anyone explain me where the warning comes from?

Share this post


Link to post
Share on other sites
RayNbow    191
Quote:
Original post by Zbyl
But can anyone explain me where the warning comes from?

It seems to be marked as a bug in the Eclipse compiler. You can suppress the warning with:
@SuppressWarnings("unchecked")



Edit: Hmm, it seems the warning is shown because of the downcast... http://www.angelikalanger.com/GenericsFAQ/FAQSections/ParameterizedTypes.html

[Edited by - RayNbow on March 18, 2006 11:28:02 AM]

Share this post


Link to post
Share on other sites
Whackjack    100
According to the J2SE 5 API spec:

(From the Cloneable interface)
By convention, classes that implement this interface should override Object.clone (which is protected) with a public method.

You just need to implement your own clone() method and call on it. You can always make a method more public, but not more private.
This is similar to the equals() method or hashcode(). The default implementation rarely is sufficient for most cases.

Share this post


Link to post
Share on other sites
SamLowry    1865
Quote:
Original post by Whackjack
According to the J2SE 5 API spec:

(From the Cloneable interface)
By convention, classes that implement this interface should override Object.clone (which is protected) with a public method.

You just need to implement your own clone() method and call on it. You can always make a method more public, but not more private.
This is similar to the equals() method or hashcode(). The default implementation rarely is sufficient for most cases.


That doesn't help him very much, because his code must work on type Object and that class doesn't define clone() as a public method.

The Cloneable interface is one of the big design monsters present in the java library. The only sensible thing you can do with it is ignore it and create your own Copyable interface. If this is not an option for the original poster, no other solution than using reflection comes to mind. (If there would be an alternative solution, it would in a sense represent another one of java's big failures)

Share this post


Link to post
Share on other sites
Whackjack    100
If he's using generics, it'll be cast to the original class by the compiler anyway.
Another way it could be done is to use System.arrayCopy(). Pull the elements out of the List using toArray(), copy them, then put them into a new List.
Or, as SamLowry said, come up with your own interface and do it that way.

Share this post


Link to post
Share on other sites
Zbyl    133
Thanks guys!
I think I'll stick to reflection - it's simple and doesn't reqiure changes in other code.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this