2011-02-02 8 views
10

Supongamos que usted tiene una referencia de tipo java.util.Collection en un método y no se puede decir lo que la aplicación de java.util.Collection va a señalar en tiempo de ejecución, es posible clonar la colección?Java: clon colección arbitraria mediante la referencia a la colección

Quería implementar un método genérico que filtrará cualquier tipo de colección dada. Por lo tanto, el método tomará java.util.Collection como entrada. Sin embargo, más allá de esto, no quería modificar la colección original, así que quería clonar la colección.

+2

¿Por qué necesita que su colección de salida sea del mismo tipo que la de entrada? –

+0

¿La colección original debe permanecer sin modificaciones? – Puce

+0

@Nicolas: cuestión de conveniencia :) – Rnet

Respuesta

2

veo tres opciones:

  1. confiar en propia clone método de la colección (suponiendo que implementa Cloneable) y luego eliminar los elementos no deseados. Editar: Como se señala en los comentarios y otras respuestas, clone() no es público y, por lo tanto, no es accesible.

  2. Pida a la persona que llama que proporcione una colección vacía para copiar los elementos de destino entre el origen y el destino.

  3. Defina una interfaz de fábrica para crear una colección vacía y solicite a la persona que llama que proporcione una implementación de fábrica. Luego, copie los elementos de destino entre el origen y el destino.

+1

ver mi comentario en michael publicar sobre el método de clonación y la interfaz cloneabl. – Nicolas

+1

Sí, el # 2 es más razonable – Rnet

0

En teoría, es posible con la reflexión, sin embargo, no todas las implementaciones Collection pueden (o deberían) crearse de esta manera. Un buen ejemplo es el resultado de Collections.singletonList(), que no tiene ningún constructor público. Otras colecciones especiales pueden plantear otros problemas también.

Lo que haría en su lugar es simplemente verificar las interfaces que implementa la colección de entrada y devolver una implementación "predeterminada" para ese tipo. Por ejemplo:

Collection c = ... 
if(c instanceof SortedSet) 
    return new TreeSet(c); 
if(c instanceof Set) 
    return new HashSet(c); 

Respuestas.

+2

Mañana cualquiera puede implementar Colección. –

0

Si la colección implementa Cloneable, puede hacerlo. No tendrías que preocuparte por el tipo exacto; la implementación de la colección clone() se encargaría de eso.

+0

Object.clone() está protegido. No puede simplemente llamar si no conoce el tipo real del objeto. Bueno, tal vez puedas hacerlo usando la reflexión como sugirió biziclop en su respuesta. –

+0

Se corrigió ese problema: se olvidó de que necesitaba implementar 'cloneable' y anular' clone() '. –

+1

Incluso si implementa 'Cloneable', el método de clonación puede no ser público, consulte javadoc http://download.oracle.com/javase/6/docs/api/java/lang/Cloneable.html – Nicolas

9

Unfortunaly la interfaz Colección no dice nada sobre la implementación de Clonable Interface.


Pero lo que siempre se puede hacer es copiar la colección:

List<T> copy = new ArrayList<T>(original); 

Si sólo quiere asegurarse de que no se modifica a continuación, se envuelve con una colección unmodidfiable en lugar de clonación es:

Collection<T> unmodifiable = Collections.unmodifiableCollection(original); 
+1

Bastantes de las implementaciones de 'Collection' do, por lo que el método' instanceof' funcionaría para muchos casos. –

+0

Mira los comentarios sobre otras respuestas: Cloneable no significa que puede usar el método de clonación. – Nicolas

+0

La referencia a la colección no modificable seguirá siendo local, la colección original aún se puede modificar mediante referencias externas. Quería modificar una copia de la colección original y devolverla. – Rnet

4

Voy a demostrar en Scala, ya que tiene un REPL donde puedo probar, pero el mismo seman los tics deberían funcionar en Java.

import java.util._ 
val orig = new LinkedList[Int] 
val theClone = orig.clone 

La Scala REPL me dice que tiene theClone estática tipo Object (se puede jugar esta a Collection[Int] o LinkedList[Int]), pero el tipo dinámico del clon es todavía LinkedList.

Ahora supongo que lo que desea es un método que devuelve un tipo estático LinkedList cuando recieves un tipo estático LinkedList y devuelve un tipo estático ArrayList cuando recieves un tipo estático ArrayList, etc., en cuyo caso

def doClone[C <: Collection[_]](orig:C) = { 
    val cloneMethod = orig.getClass.getDeclaredMethod("clone") 
    if (cloneMethod.isAccessible) 
    cloneMethod.invoke(orig).asInstanceOf[C] 
    else 
    throw new CloneNotSupportedException 
} 

en Java, creo que es

<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(); 
} 
1

mejor filtro de la colección mediante su modificación, en su método. Hasta la persona que llama para proporcionarle la colección original o una copia adecuada de la misma.

3

Si realmente, realmente, realmente, realmente necesitas hacer esto, hay un hack feo.

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; 
    } 
+3

FYI, esto viene de aquí: http://code.google.com/p/google-collections/source/browse/trunk/src/com/google/ common/base/Objects.java? spec = svn16 & r = 9 - luego lo borramos, porque es un truco hacky sucio inútil y terrible. –

Cuestiones relacionadas