Reputation: 5040
Suppose you've a reference of type java.util.Collection
in a method and cannot say what implementation of java.util.Collection
will it point to at run time, is it possible to clone the Collection?
I wanted to implement a generic method which will filter any type of collection given. Hence the method will take java.util.Collection
as input. However beyond this, I didn't want to modify the original collection, so I wanted to clone the collection.
Upvotes: 10
Views: 8964
Reputation: 40851
If you really, really, really, really need to do this, there is an ugly hack.
public static <T> T tryToClone(T object)
throws CloneNotSupportedException {
Object clone = null;
// Use reflection, because there is no other way
try {
Method method = object.getClass().getMethod("clone");
clone = method.invoke(object);
} catch (InvocationTargetException e) {
rethrow(e.getCause());
} catch (Exception cause) {
rethrow(cause);
}
if (object.getClass().isInstance(clone)) {
@SuppressWarnings("unchecked") // clone class <= object class <= T
T t = (T) clone;
return t;
} else {
throw new ClassCastException(clone.getClass().getName());
}
}
private static void rethrow(Throwable cause)
throws CloneNotSupportedException {
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
if (cause instanceof CloneNotSupportedException) {
throw (CloneNotSupportedException) cause;
}
CloneNotSupportedException e = new CloneNotSupportedException();
e.initCause(cause);
throw e;
}
Upvotes: 3
Reputation: 58770
I'm going to demonstrate in Scala, becuase it has a REPL where I can test, but the same semantics should work in Java.
import java.util._
val orig = new LinkedList[Int]
val theClone = orig.clone
The Scala REPL tells me that theClone has static type Object
(you can cast this to Collection[Int]
or LinkedList[Int]
), but the dynamic type of the clone is still LinkedList
.
Now I suppose what you want is a method that returns a static type LinkedList
when it recieves a static type LinkedList
and returns a static type ArrayList
when it recieves a static type ArrayList
, etc. in which case
def doClone[C <: Collection[_]](orig:C) = {
val cloneMethod = orig.getClass.getDeclaredMethod("clone")
if (cloneMethod.isAccessible)
cloneMethod.invoke(orig).asInstanceOf[C]
else
throw new CloneNotSupportedException
}
In Java, I think that's
<C extends Collection<?> > C doClone (C orig) {
java.lang.reflect.Method cloneMethod =
orig.getClass().getDeclaredMethod("clone");
if (cloneMethod.isAccessible())
return (C) cloneMethod.invoke(orig);
else
throw new CloneNotSupportedException();
}
Upvotes: 4
Reputation: 9265
Better filter the collection by modifying it in your method. Up to the caller to provide you with the original collection or a proper copy of it.
Upvotes: 1
Reputation: 120791
Unfortunaly the interface Collection does not say anything about implementing Clonable Interface.
But what you can always do is copy the collection:
List<T> copy = new ArrayList<T>(original);
If you only want to make sure that it is not modified then wrap it with an unmodidfiable collection instead of cloning it:
Collection<T> unmodifiable = Collections.unmodifiableCollection(original);
Upvotes: 9
Reputation: 87533
I see three options:
Rely on the collection's own Edit: As pointed out in the comments and other answers, clone
method (assuming it implements Cloneable
) and then remove the undesired elements.clone()
is not public and therefore is not accessible.
Ask the caller to provide an empty collection to copy the target elements between source and destination.
Define a factory interface to create an empty collection and ask the caller to provide a factory implementation. Then copy the target elements between source and destination.
Upvotes: 2
Reputation: 3347
If the collection implements Cloneable
, you can do it. You wouldn't have to worry about the exact type; the collection's clone()
implementation would take care of that.
Upvotes: 0
Reputation: 49744
In theory it is possible with reflection, however not all Collection
implementations can (or should) be instantiated this way. A prime example is a result of Collections.singletonList()
, which has no public constructors at all. Other special collections can raise other issues too.
What I would do instead is to just check the interfaces the input collection implements and return a "default" implementation for that type. So for example:
Collection c = ...
if( c instanceof SortedSet )
return new TreeSet( c );
if( c instanceof Set )
return new HashSet( c );
Ans so on.
Upvotes: 0