2011-02-02 8 views
10

Supponiamo di avere un riferimento di tipo java.util.Collection in un metodo e non si può dire quale implementazione di java.util.Collection indicherà in fase di esecuzione, è possibile clonare la raccolta?Java: clona raccolta arbitraria tramite riferimento alla raccolta

Ho voluto implementare un metodo generico che filtrerà qualsiasi tipo di raccolta dati. Quindi il metodo richiederà java.util.Collection come input. Comunque oltre a questo, non volevo modificare la collezione originale, quindi volevo clonare la collezione.

+2

Perché è necessario che la raccolta di output sia dello stesso tipo di quella di input? –

+0

La collezione originale dovrebbe rimanere inalterata? – Puce

+0

@Nicolas: Matter of convenience :) – Rnet

risposta

2

vedo tre opzioni:

  1. contare su proprio clone metodo della collezione (ammesso che implementa Cloneable) e quindi rimuovere gli elementi indesiderati. Modifica: Come indicato nei commenti e altre risposte, clone() non è pubblico e pertanto non è accessibile.

  2. Chiedere al chiamante di fornire una raccolta vuota per copiare gli elementi di destinazione tra origine e destinazione.

  3. Definire un'interfaccia di fabbrica per creare una raccolta vuota e chiedere al chiamante di fornire un'implementazione di fabbrica. Quindi copia gli elementi di destinazione tra l'origine e la destinazione.

+1

vedere il mio commento su michael post sul metodo clone e l'interfaccia cloneabl. – Nicolas

+1

Sì, il numero 2 è più ragionevole – Rnet

0

In teoria è possibile con la riflessione, tuttavia non tutte le implementazioni Collection possono (o dovrebbero) essere istanziate in questo modo. Un primo esempio è il risultato di Collections.singletonList(), che non ha affatto costruttori pubblici. Altre raccolte speciali possono sollevare anche altri problemi.

Quello che vorrei fare è semplicemente controllare le interfacce degli strumenti di raccolta input e restituire un'implementazione "predefinita" per quel tipo. Quindi, ad esempio:

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

Ans così via.

+2

Domani chiunque può implementare la Collezione. –

0

Se la raccolta implementa Cloneable, è possibile farlo. Non dovresti preoccuparti del tipo esatto; l'implementazione della collezione clone() si occuperebbe di ciò.

+0

Object.clone() è protetto. Non puoi semplicemente chiamarlo se non conosci il tipo reale dell'oggetto. Bene, forse puoi farlo usando la riflessione come suggeriva il biziclop nella sua risposta. –

+0

Risolto il problema - ho dimenticato che era necessario implementare 'cloneable' e sovrascrivere' clone() '. –

+1

Anche se si implementa 'Cloneable', il metodo clone potrebbe non essere pubblico, vedere javadoc http://download.oracle.com/javase/6/docs/api/java/lang/Cloneable.html – Nicolas

9

Purtroppo l'interfaccia Collection non dice nulla sull'implementazione di Clonable Interface.


Ma quello che sempre si può fare è copiare la collezione:

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

Se si desidera solo fare in modo che non venga modificato poi avvolgere con una collezione unmodidfiable invece di clonazione iT:

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

Alcune delle implementazioni di 'Collection' do, quindi il metodo' instanceof' potrebbe funzionare in molti casi. –

+0

Guarda il commento su altre risposte: Cloneable non significa che puoi usare il metodo clone. – Nicolas

+0

Il riferimento alla collezione non modificabile rimarrà locale, la collezione originale è ancora modificabile da riferimenti esterni, volevo modificare una copia della collezione originale e restituirla. – Rnet

4

ho intenzione di dimostrare a Scala, becuase ha un REPL dove posso provare, ma lo stesso Seman i tics dovrebbero funzionare in Java.

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

La Scala REPL mi dice che ha theClone tipo statico Object (si può lanciare questo per Collection[Int] o LinkedList[Int]), ma il tipo dinamico del clone è ancora LinkedList.

Ora suppongo quello che vuoi è un metodo che restituisce un tipo statico LinkedList quando si riceve un tipo statico LinkedList e restituisce un tipo statico ArrayList quando si riceve un tipo statico ArrayList, ecc nel qual 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 
} 

in Java, io credo che sia

<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

Meglio filtro della collezione modificandolo nel metodo. Fino al chiamante per fornire la collezione originale o una copia adeguata di esso.

3

Se davvero, davvero, davvero, davvero bisogno di fare questo, c'è un brutto scherzo.

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, questo proviene da qui: http://code.google.com/p/google-collections/source/browse/trunk/src/com/google/ common/base/Objects.java? spec = svn16 & r = 9 - in seguito lo abbiamo cancellato, perché è un terribile e inutile hack non funzionante. –