Rnet
Rnet

Reputation: 5040

Java: clone arbitrary Collection through reference to Collection

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

Answers (7)

Kevin Bourrillion
Kevin Bourrillion

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

Ken Bloom
Ken Bloom

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

Nicolas Repiquet
Nicolas Repiquet

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

Ralph
Ralph

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

Bert F
Bert F

Reputation: 87533

I see three options:

  1. Rely on the collection's own clone method (assuming it implements Cloneable) and then remove the undesired elements. Edit: As pointed out in the comments and other answers, clone() is not public and therefore is not accessible.

  2. Ask the caller to provide an empty collection to copy the target elements between source and destination.

  3. 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

Michael K
Michael K

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

biziclop
biziclop

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

Related Questions