2012-12-02 19 views
10

Stavo leggendo per eseguire correttamente una copia profonda di un array, tuttavia ero confuso su come è stato implementato lo #clone(). Si tratta di un membro della classe java.lang.Object, e tuttavia se si leggono le javadocs:Perché #clone() non è nell'interfaccia Cloneable?

In primo luogo, se la classe di questo oggetto non implementa l'interfaccia Cloneable, poi un CloneNotSupportedException è gettato.

Quindi perché definire il metodo clone in primo luogo? Sicuramente se un metodo può essere utilizzato solo quando è presente un'interfaccia, devi inserire il metodo nell'interfaccia. L'interfaccia Cloneable è vuota; è solo un'interfaccia marker utilizzata da Java per garantire che l'utilizzo del metodo clone sia legale.

Facendo in questo modo rimuove anche la capacità di fare uso di farmaci generici al fine di garantire la sicurezza Tipo:

class Foo implements Cloneable { // Valid. 
    @Override 
    public Object clone() throws CloneNotSupportedException { 
     // ... 
    } 
} 

class TypeSafeFoo implements Cloneable<TypeSafeFoo> { // Not valid. 
    @Override 
    public TypeSafeFoo clone() throws CloneNotSupportedException { 
     // ... 
    } 
} 

Perché ha fatto Java in questo modo? Sono sicuro che hanno motivi legittimi, ma non riesco a capirlo.

+3

In realtà questo era un difetto di progettazione come detto da Joshua Bloch –

+0

Questo è uno dei tanti motivi per clonazione è considerato rotto. –

risposta

14

Il contratto di clonazione in Java stabilisce che ogni implementazione clone deve prima ottenere l'istanza clonata da super.clone(). Ciò crea una catena che termina sempre con la chiamata a Object.clone e tale metodo contiene codice "magico" di livello nativo che crea una copia binaria del grezzo sottostante struct che rappresenta l'oggetto Java. Se questo meccanismo non esistesse, clone non sarebbe polimorfo: il metodo Object.clone produce un'istanza di qualunque classe venga chiamata; questo non può essere riprodotto senza codice nativo.

Ecco perché non è stato possibile evitare il metodo Object.clone. Cloneable potrebbe contenere un metodo clone, ma creerebbe problemi relativi alla clausola throws. Il modo in cui ti trovi sei libero di dichiarare clone senza eccezioni dichiarate, o di dichiarare eccezioni arbitrarie. Questa flessibilità non sarebbe possibile se il metodo fosse già dichiarato nell'interfaccia.

Tenere presente che Generics sarebbe poco utile per la clonazione: immagini protected T clone() in Object: da dove proviene lo T? Avremmo bisogno di Object<T> e forzare ogni classe nell'universo Java da parametrizzare su se stesso, e tutto questo solo per rendere questo meccanismo semi-deprecato un po 'meglio?Anche tenere a mente che questo codice è perfettamente legale:

public class TheMightyOne implements Cloneable { 
    @Override public TheMightyOne clone() { 
    return (TheMightyOne) super.clone(); 
    } 
} 

Si può chiamare:

TheMightyOne one = new TheMightyOne(); 
TheMightyOne two = one.clone(); // do downcasts needed 
+0

Non è stato possibile evitare la maggior parte di ciò rendendo statico il metodo del clone magico? 'Object.clone (Cloneable item)'. Quindi qualsiasi cosa che implementa 'Cloneable' passerebbe' this' a 'Object.clone'. Questo avrebbe potuto facilmente funzionare anche con i generici. –

4

Per gestire la copia del clone e del campo base, il clone deve ereditare un'implementazione del metodo. Una classe Cloneable può essere visualizzata in qualsiasi punto della gerarchia e potrebbe essere necessario estendere una superclasse specifica per eseguire il lavoro principale. L'unica superclasse da cui tutte le possibili classi Clonabili possono ereditare l'implementazione è Object.

La clonazione è stata definita molto prima dei farmaci generici.

0

clone() metodo è inutile, ogni oggetto potrebbe invocare Object.clone metodo da riflettere, in modo che un oggetto può essere clonato dipende dall'implementazione dell'interfaccia Cloneable. Questo è per un motivo di sicurezza. Si può semplicemente clonare un oggetto che implementa Cloneable da questa util:

@SuppressWarnings("unchecked") 
public static <T extends Cloneable> T clone(Cloneable obj) { 
    T rtn = null; 
    try { 
     Method method = Object.class.getDeclaredMethod("clone"); 
     method.setAccessible(true); 
     rtn = (T) method.invoke(obj); 
    } catch (Throwable t) { 
     t.printStackTrace(); 
    } 
    return rtn; 
} 
Problemi correlati