Perché nessun ICloneable <T>?


223

C'è un motivo particolare per cui ICloneable<T>non esiste un generico ?

Sarebbe molto più comodo, se non avessi bisogno di lanciarlo ogni volta che clonavo qualcosa.


6
No! con tutto il dovuto rispetto per i "motivi", sono d'accordo con te, avrebbero dovuto attuarlo!
Shimmy Weitzhandler,

Sarebbe stato carino definire Microsoft (il problema con le interfacce brew-your-own è che le interfacce in due assiemi diversi saranno incompatibili, anche se sono semanticamente identiche). Se avessi progettato l'interfaccia, avrebbe avuto tre membri, Clone, Self e CloneIfMutable, ognuno dei quali avrebbe restituito una T (l'ultimo membro avrebbe restituito Clone o Self, a seconda dei casi). Il membro Self renderebbe possibile accettare un ICloneable (di Foo) come parametro e quindi usarlo come Foo, senza la necessità di un typecast.
supercat

Ciò consentirebbe una corretta gerarchia di classi di clonazione, in cui le classi ereditabili espongono un metodo "clone" protetto e dispongono di derivati ​​sigillati che ne espongono uno pubblico. Ad esempio, si potrebbe avere Pile, CloneablePile: Pile, EnhancedPile: Pile e CloneableEnhancedPile: EnhancedPile, nessuno dei quali sarebbe rotto se clonato (anche se non tutti espongono un metodo di clonazione pubblico), e FurtherEnhancedPile: EnhancedPile (che sarebbe rotto se clonato, ma non espone alcun metodo di clonazione). Una routine che accetta un ICloneable (di Pile) potrebbe accettare un CloneablePile o un CloneableEnhancedPile ...
supercat

... anche se CloneableEnhancedPile non eredita da CloneablePile. Si noti che se EnhancedPile ereditato da CloneablePile, FurtherEnhancedPile dovrebbe esporre un metodo di clonazione pubblica e potrebbe essere passato a codice che si aspetterebbe di clonarlo, violando il principio di sostituibilità di Liskov. Poiché CloneableEnhancedPile implementerebbe ICloneable (Of EnhancedPile) e implicitamente ICloneable (Of Pile), potrebbe essere passato a una routine in attesa di un derivato clonabile di Pile.
supercat

Risposte:


111

ICloneable è ora considerata una cattiva API, poiché non specifica se il risultato è una copia profonda o superficiale. Penso che sia per questo che non migliorano questa interfaccia.

Probabilmente puoi fare un metodo di estensione della clonazione tipizzato, ma penso che richiederebbe un nome diverso poiché i metodi di estensione hanno meno priorità rispetto a quelli originali.


8
Non sono d'accordo, cattivo o non cattivo, a volte è molto utile per la clonazione di SHALLOW, e in quei casi è davvero necessario il Clone <T>, per salvare un boxing e unboxing non necessari.
Shimmy Weitzhandler,

2
@AndreyShchekin: Cosa non è chiaro sulla clonazione profonda vs superficiale? Se List<T>avesse un metodo clone, mi aspetterei che producesse un i List<T>cui elementi hanno le stesse identità di quelli dell'elenco originale, ma mi aspetto che qualsiasi struttura di dati interna venga duplicata in base alle necessità per garantire che nulla influisca su un elenco le identità degli oggetti archiviati nell'altro. Dov'è l'ambiguità? Un problema più grande con la clonazione arriva con una variazione del "problema dei diamanti": se CloneableFooeredita da [non clonabile pubblicamente] Foo, dovrebbe CloneableDerivedFooderivare da ...
supercat

1
@supercat: non posso dire di aver compreso appieno le tue aspettative, come considererai un identityelenco dell'elenco stesso, ad esempio (nel caso di un elenco di elenchi)? Tuttavia, ignorando ciò, le tue aspettative non sono l'unica idea possibile che le persone possano avere quando chiamano o implementano Clone. Che cosa succede se gli autori delle biblioteche che implementano un altro elenco non seguono le tue aspettative? L'API dovrebbe essere banalmente inequivocabile, non discutibilmente inequivocabile.
Andrey Shchekin l'

2
@supercat Quindi stai parlando di copia superficiale. Tuttavia, non c'è nulla di fondamentalmente sbagliato in una copia profonda, ad esempio se si desidera eseguire una transazione reversibile su oggetti in memoria. Le tue aspettative si basano sul tuo caso d'uso, altre persone potrebbero avere aspettative diverse. Se mettono i tuoi oggetti nei loro oggetti (o viceversa), i risultati saranno inaspettati.
Andrey Shchekin,

5
@supercat non prendi in considerazione che fa parte di .NET framework, non fa parte della tua applicazione. quindi se crei una classe che non esegue la clonazione in profondità, qualcun altro crea una classe che esegue la clonazione in profondità e chiama Clonetutte le sue parti, non funzionerà in modo prevedibile, a seconda che quella parte sia stata implementata da te o da quella persona a chi piace la clonazione profonda. il tuo punto sui modelli è valido ma avere IMHO nell'API non è abbastanza chiaro - o dovrebbe essere chiamato ShallowCopyper sottolineare il punto, o non fornito affatto.
Andrey Shchekin,

141

Oltre alla risposta di Andrey (che sono d'accordo con, +1) - quando ICloneable è fatto, si può anche scegliere esplicito implementazione per rendere il pubblico Clone()di ritorno un oggetto tipizzato:

public Foo Clone() { /* your code */ }
object ICloneable.Clone() {return Clone();}

Naturalmente c'è un secondo problema con una ICloneable<T>eredità generica .

Se ho:

public class Foo {}
public class Bar : Foo {}

E ho implementato ICloneable<T>, quindi ho implementato ICloneable<Foo>? ICloneable<Bar>? Inizi rapidamente a implementare molte interfacce identiche ... Confronta con un cast ... ed è davvero così male?


10
+1 Ho sempre pensato che il vero problema della covarianza fosse la vera ragione
Tamas Czinege,

C'è un problema con questo: l'implementazione esplicita dell'interfaccia deve essere privata , il che provocherebbe problemi se ad un certo punto Foo dovesse essere lanciato su ICloneable ... Mi sto perdendo qualcosa qui?
Gioele a Gö

3
Il mio errore: si scopre che, nonostante sia definito come privato, il metodo verrà chiamato se Foo dovesse essere lanciato su IClonable (CLR tramite C # 2nd Ed, p.319). Sembra una strana decisione progettuale, ma è così. Quindi Foo.Clone () fornisce il primo metodo e ((ICloneable) Foo) .Clone () fornisce il secondo metodo.
Gioele a Gö

In realtà, le implementazioni esplicite dell'interfaccia fanno parte dell'API pubblica. In quanto sono richiamabili pubblicamente tramite casting.
Marc Gravell

8
La soluzione proposta sembra ottima all'inizio ... finché non ti rendi conto che in una gerarchia di classi, vuoi che "public Foo Clone ()" sia virtuale e sovrascritto nelle classi derivate in modo che possano restituire il loro tipo (e clonare i loro campi di corso). Purtroppo, non è possibile modificare il tipo restituito in una sostituzione (il problema di covarianza menzionato). Quindi, tornerai al problema originale di nuovo: l'implementazione esplicita non ti compra molto (a parte restituire il tipo di base della tua gerarchia invece di "oggetto") e sei tornato a trasmettere la maggior parte del tuo Risultati della chiamata Clone (). Che schifo!
Ken Beckett,

18

Devo chiederti, cosa faresti esattamente con l'interfaccia oltre a implementarla? Le interfacce sono in genere utili solo quando si esegue il cast su di essa (ad esempio, questa classe supporta "IBar") o hanno parametri o setter che la accettano (ovvero, prendo una "IBar"). Con ICloneable - abbiamo esaminato l'intero Framework e non siamo riusciti a trovare un singolo utilizzo ovunque che fosse qualcosa di diverso da una sua implementazione. Inoltre, non siamo riusciti a trovare alcun utilizzo nel "mondo reale" che faccia qualcosa di diverso dall'implementarlo (nelle ~ 60.000 app a cui abbiamo accesso).

Ora, se vuoi solo applicare un modello che desideri implementare i tuoi oggetti "clonabili", è un uso del tutto perfetto - e vai avanti. Puoi anche decidere esattamente cosa significa "clonazione" per te (cioè profondo o superficiale). Tuttavia, in tal caso, non è necessario per noi (il BCL) definirlo. Definiamo astrazioni nel BCL solo quando è necessario scambiare istanze tipizzate come tale astrazione tra librerie non correlate.

David Kean (BCL Team)


1
ICloneable non generico non è molto utile, ma ICloneable<out T>potrebbe essere molto utile se ereditato da ISelf<out T>, con un solo metodo Selfdi tipo T. Uno spesso non ha bisogno di "qualcosa che sia clonabile", ma potrebbe benissimo aver bisogno Tdi qualcosa che sia clonabile. Se un oggetto clonabile implementa ISelf<itsOwnType>, una routine che necessita di un Tclonabile può accettare un parametro di tipo ICloneable<T>, anche se non tutti i derivati ​​clonabili Tcondividono un antenato comune.
supercat

Grazie. È simile alla situazione del cast che ho menzionato sopra (cioè "questa classe supporta" IBar "). Purtroppo, abbiamo solo escogitato scenari molto limitati e isolati in cui faresti effettivamente uso del fatto che T sia clonabile. Hai in mente delle situazioni?
David Kean

Una cosa che a volte è imbarazzante in .net è la gestione di raccolte mutabili quando si suppone che il contenuto di una raccolta sia visto come un valore (vale a dire che vorrò conoscere in seguito l'insieme di elementi presenti nella raccolta). Qualcosa del genere ICloneable<T>potrebbe essere utile per questo, sebbene un quadro più ampio per il mantenimento di classi mutabili e immutabili parallele potrebbe essere più utile. In altre parole, il codice che ha bisogno di vedere che cosa Foocontiene un certo tipo ma non lo cambierà né si aspetta che non cambierà mai potrebbe usare un IReadableFoo, mentre ...
supercat

... il codice che vuole contenere il contenuto di Foopotrebbe usare un ImmutableFoocodice while che per manipolarlo potrebbe usare un MutableFoo. Il codice fornito con qualsiasi tipo di IReadableFoodovrebbe essere in grado di ottenere una versione mutabile o immutabile. Un tale quadro sarebbe bello, ma sfortunatamente non riesco a trovare un modo carino per sistemare le cose in modo generico. Se esistesse un modo coerente per creare un wrapper di sola lettura per una classe, una cosa del genere potrebbe essere utilizzata in combinazione con ICloneable<T>una copia immutabile di una classe che contiene T".
supercat

@supercat Se vuoi clonare un List<T>, in modo tale che il clonato List<T>sia una nuova raccolta che trattiene i puntatori a tutti gli stessi oggetti nella raccolta originale, ci sono due semplici modi per farlo senza ICloneable<T>. Il primo è il Enumerable.ToList()metodo di estensione: List<foo> clone = original.ToList();il secondo è il List<T>costruttore che accetta un IEnumerable<T>: List<foo> clone = new List<foo>(original);sospetto che il metodo di estensione stia probabilmente chiamando il costruttore, ma entrambi faranno ciò che stai richiedendo. ;)
CptRobby,

12

Penso che la domanda "perché" sia inutile. Esistono molte interfacce / classi / ecc ... che sono molto utili, ma non fanno parte della libreria di base di .NET Frameworku.

Ma soprattutto puoi farlo da solo.

public interface ICloneable<T> : ICloneable {
    new T Clone();
}

public abstract class CloneableBase<T> : ICloneable<T> where T : CloneableBase<T> {
    public abstract T Clone();
    object ICloneable.Clone() { return this.Clone(); }
}

public abstract class CloneableExBase<T> : CloneableBase<T> where T : CloneableExBase<T> {
    protected abstract T CreateClone();
    protected abstract void FillClone( T clone );
    public override T Clone() {
        T clone = this.CreateClone();
        if ( object.ReferenceEquals( clone, null ) ) { throw new NullReferenceException( "Clone was not created." ); }
        return clone
    }
}

public abstract class PersonBase<T> : CloneableExBase<T> where T : PersonBase<T> {
    public string Name { get; set; }

    protected override void FillClone( T clone ) {
        clone.Name = this.Name;
    }
}

public sealed class Person : PersonBase<Person> {
    protected override Person CreateClone() { return new Person(); }
}

public abstract class EmployeeBase<T> : PersonBase<T> where T : EmployeeBase<T> {
    public string Department { get; set; }

    protected override void FillClone( T clone ) {
        base.FillClone( clone );
        clone.Department = this.Department;
    }
}

public sealed class Employee : EmployeeBase<Employee> {
    protected override Employee CreateClone() { return new Employee(); }
}

Qualsiasi metodo di clonazione praticabile per una classe ereditabile deve utilizzare Object.MemberwiseClone come punto di partenza (oppure utilizzare la riflessione) poiché altrimenti non esiste alcuna garanzia che il clone sarà dello stesso tipo dell'oggetto originale. Ho un modello abbastanza carino se sei interessato.
supercat,

@supercat perché non farlo una risposta allora?
nawfal,

"Esistono molte interfacce / classi / ecc ... che sono molto utili, ma non fanno parte della libreria di base di .NET Frameworku." Puoi darci degli esempi?
Krythic il

1
@Krythic ie: strutture dati avanzate come b-tree, red-black tree, circle buffer. Nel '09 non c'erano tuple, riferimenti generici deboli, raccolte simultanee ...
TcKs

10

È abbastanza facile scrivere l'interfaccia da soli se ne hai bisogno:

public interface ICloneable<T> : ICloneable
        where T : ICloneable<T>
{
    new T Clone();
}

Ho incluso uno snippet poiché a causa dell'onore dei diritti di copia il link è stato interrotto ...
Shimmy Weitzhandler

2
E come dovrebbe funzionare esattamente questa interfaccia? Affinché il risultato di un'operazione di clonazione sia castabile al tipo T, l'oggetto da clonare deve derivare da T. Non c'è modo di impostare un tale vincolo di tipo. Realisticamente parlando, l'unica cosa che posso vedere il risultato del ritorno di iCloneable è un tipo iCloneable.
supercat,

@supercat: sì, è esattamente quello che restituisce Clone (): una T che implementa ICloneable <T>. MbUnit utilizza questa interfaccia da anni , quindi sì, funziona. Per quanto riguarda l'implementazione, dai un'occhiata alla fonte di MbUnit.
Mauricio Scheffer,

2
@Mauricio Scheffer: vedo cosa sta succedendo. Nessun tipo implementa effettivamente iCloneable (di T). Invece, ogni tipo implementa iCloneable (di per sé). Funzionerebbe, immagino, anche se tutto ciò che fa è spostare un typecast. È un peccato che non ci sia modo di dichiarare che un campo ha un vincolo di eredità e uno di interfaccia; sarebbe utile che un metodo di clonazione restituisse un oggetto di un tipo base semi-clonabile (clonazione esposta solo come metodo protetto), ma con un vincolo di tipo clonabile. Ciò consentirebbe ad un oggetto di accettare derivati ​​clonabili di derivati ​​semi-clonabili di un tipo base.
supercat,

@Mauricio Scheffer: A proposito, suggerirei che ICloneable (di T) supporti anche una proprietà di sola lettura (di ritorno di tipo T). Ciò consentirebbe a un metodo di accettare un ICloneable (di Foo) e di usarlo (tramite la proprietà Self) come Foo.
supercat

9

Avendo letto di recente l'articolo Perché copiare un oggetto è una cosa terribile da fare? , Penso che questa domanda abbia bisogno di ulteriori clafirication. Altre risposte qui forniscono buoni consigli, ma la risposta non è completa - perché no ICloneable<T>?

  1. uso

    Quindi, hai una classe che la implementa. Mentre prima avevi un metodo che desiderava ICloneable, ora deve essere generico da accettare ICloneable<T>. Dovresti modificarlo.

    Quindi, potresti avere un metodo che controlla se un oggetto is ICloneable. E adesso? Non puoi farlo is ICloneable<>e poiché non conosci il tipo di oggetto al tipo di compilazione, non puoi rendere generico il metodo. Primo vero problema.

    Quindi devi avere entrambi ICloneable<T>e ICloneable, il primo implementando il secondo. Quindi un implementatore dovrebbe implementare entrambi i metodi - object Clone()e T Clone(). No, grazie, ci siamo già già divertiti abbastanza IEnumerable.

    Come già sottolineato, esiste anche la complessità dell'eredità. Mentre la covarianza può sembrare risolvere questo problema, un tipo derivato deve implementare ICloneable<T>il proprio tipo, ma esiste già un metodo con la stessa firma (= parametri, in sostanza) - quello Clone()della classe base. Rendere esplicita la tua nuova interfaccia del metodo clone è inutile, perderai il vantaggio che hai cercato durante la creazione ICloneable<T>. Quindi aggiungi la newparola chiave. Ma non dimenticare che dovresti anche sovrascrivere la classe base ' Clone()(l'implementazione deve rimanere uniforme per tutte le classi derivate, cioè restituire lo stesso oggetto da ogni metodo clone, quindi deve essere il metodo clone base virtual)! Ma, sfortunatamente, non è possibile entrambioverride enewmetodi con la stessa firma. Scegliendo la prima parola chiave, si perderebbe l'obiettivo che si desidera avere quando si aggiunge ICloneable<T>. Lanciando il secondo, si romperà l'interfaccia stessa, facendo metodi che dovrebbero fare lo stesso restituire oggetti diversi.

  2. Punto

    Desiderate il ICloneable<T>comfort, ma il comfort non è ciò per cui sono progettate le interfacce, il loro significato è (in generale OOP) unificare il comportamento degli oggetti (sebbene in C # sia limitato all'unificazione del comportamento esteriore, ad esempio i metodi e le proprietà, non il loro funzionamento).

    Se il primo motivo non ti ha ancora convinto, potresti obiettare che ICloneable<T>potrebbe funzionare anche in modo restrittivo, per limitare il tipo restituito dal metodo clone. Tuttavia, il programmatore cattivo può implementare ICloneable<T>dove T non è il tipo che lo sta implementando. Quindi, per ottenere la tua restrizione, puoi aggiungere un bel vincolo al parametro generico:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    Certamente più restrittivo di quello senza where, non puoi ancora limitare che T è il tipo che sta implementando l'interfaccia (puoi derivare da ICloneable<T>un tipo diverso che lo implementa).

    Vedete, anche questo scopo non può essere raggiunto (anche l'originale ICloneablefallisce, nessuna interfaccia può davvero limitare il comportamento della classe di implementazione).

Come puoi vedere, questo dimostra che rendere l'interfaccia generica sia difficile da implementare completamente, ma anche non necessaria e inutile.

Ma tornando alla domanda, quello che cerchi veramente è avere conforto durante la clonazione di un oggetto. Ci sono due modi per farlo:

Metodi aggiuntivi

public class Base : ICloneable
{
    public Base Clone()
    {
        return this.CloneImpl() as Base;
    }

    object ICloneable.Clone()
    {
        return this.CloneImpl();
    }

    protected virtual object CloneImpl()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public new Derived Clone()
    {
        return this.CloneImpl() as Derived;
    }

    protected override object CloneImpl()
    {
        return new Derived();
    }
}

Questa soluzione offre agli utenti comfort e comportamento previsto, ma è anche troppo lunga da implementare. Se non volessimo che il metodo "comodo" restituisse il tipo corrente, è molto più facile averlo public virtual object Clone().

Vediamo quindi la soluzione "definitiva": cosa in C # è veramente intenzionato a darci conforto?

Metodi di estensione!

public class Base : ICloneable
{
    public virtual object Clone()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public override object Clone()
    {
        return new Derived();
    }
}

public static T Copy<T>(this T obj) where T : class, ICloneable
{
    return obj.Clone() as T;
}

Si chiama Copia per non scontrarsi con gli attuali metodi Clone (il compilatore preferisce i metodi dichiarati del tipo rispetto a quelli di estensione). Il classvincolo è lì per la velocità (non richiede controllo null ecc.).

Spero che questo chiarisca il motivo per cui non farlo ICloneable<T>. Tuttavia, si consiglia di non implementare ICloneableaffatto.


Appendice n. 1: L'unico utilizzo possibile di un generico ICloneableè per i tipi di valore, in cui potrebbe eludere il boxing del metodo Clone e implica che il valore non sia unbox. E poiché le strutture possono essere clonate (in modo superficiale) automaticamente, non è necessario implementarle (a meno che non si specifichi che significa copia profonda).
IllidanS4 vuole che Monica torni il

2

Sebbene la domanda sia molto vecchia (a 5 anni dalla stesura di questa risposta :) ed è già stata risolta, ma ho trovato questo articolo che risponde abbastanza bene alla domanda, controlla qui

MODIFICARE:

Ecco la citazione dall'articolo che risponde alla domanda (assicurati di leggere l'articolo completo, include altre cose interessanti):

Ci sono molti riferimenti su Internet che rimandano a un post del blog 2003 di Brad Abrams - all'epoca impiegato in Microsoft - in cui vengono discusse alcune considerazioni su ICloneable. La voce del blog è disponibile a questo indirizzo: Implementazione di ICloneable . Nonostante il titolo fuorviante, questo post sul blog invita a non implementare ICloneable, principalmente a causa della confusione superficiale / profonda. L'articolo termina con un semplice suggerimento: se hai bisogno di un meccanismo di clonazione, definisci la tua metodologia Clone o Copy e assicurati di documentare chiaramente se si tratta di una copia profonda o superficiale. Un modello appropriato è:public <type> Copy();


1

Un grosso problema è che non potevano limitare T alla stessa classe. Per esempio cosa ti impedirebbe di fare questo:

interface IClonable<T>
{
    T Clone();
}

class Dog : IClonable<JackRabbit>
{
    //not what you would expect, but possible
    JackRabbit Clone()
    {
        return new JackRabbit();
    }

}

Hanno bisogno di una limitazione dei parametri come:

interfact IClonable<T> where T : implementing_type

8
Non sembra male come class A : ICloneable { public object Clone() { return 1; } /* I can return whatever I want */ }
Nikola Novak,

Non vedo davvero quale sia il problema. Nessuna interfaccia può forzare le implementazioni a comportarsi in modo ragionevole. Anche se ICloneable<T>potesse costringere Tad abbinare il proprio tipo, ciò non costringerebbe un'implementazione di Clone()restituire qualcosa di remoto simile all'oggetto su cui è stato clonato. Inoltre, suggerirei che se si utilizza la covarianza di interfaccia, potrebbe essere meglio avere classi che implementano ICloneableessere sigillate, che l'interfaccia ICloneable<out T>includa una Selfproprietà che dovrebbe restituire se stessa e ...
supercat

... fare in modo che i consumatori lancino o vincolino a ICloneable<BaseType>o ICloneable<ICloneable<BaseType>>. Il BaseTypein questione dovrebbe avere un protectedmetodo per la clonazione, che sarebbe chiamato dal tipo che implementa ICloneable. Questo design consentirebbe la possibilità che si possa desiderare di avere a Container, a CloneableContainer, a FancyContainere a CloneableFancyContainer, essendo quest'ultimo utilizzabile nel codice che richiede una derivazione clonabile Containero che richiede un FancyContainer(ma non gli importa se è clonabile).
Supercat,

Il motivo per cui preferirei un tale progetto è che la questione se un tipo possa essere clonato in modo sensato è in qualche modo ortogonale ad altri aspetti di esso. Ad esempio, si potrebbe avere un FancyListtipo che potrebbe essere clonato in modo ragionevole, ma un derivato potrebbe automaticamente mantenere il suo stato in un file su disco (specificato nel costruttore). Il tipo derivato non poteva essere clonato, perché il suo stato sarebbe essere collegato a quello di un Singleton mutabile (il file), ma che l'uso non dovrebbero precludere del tipo derivato in posti che hanno bisogno la maggior parte delle caratteristiche di una FancyList, ma non avrebbe avuto bisogno per clonarlo.
Supercat,

+1: questa risposta è l'unica tra quelle che ho visto qui che risponde davvero alla domanda.
IllidanS4 vuole che Monica torni il

0

È un'ottima domanda ... Puoi crearne una tua, però:

interface ICloneable<T> : ICloneable
{
  new T Clone ( );
}

Andrey dice che è considerata una cattiva API, ma non ho sentito nulla su questa interfaccia diventare deprecata. E ciò spezzerebbe tonnellate di interfacce ... Il metodo Clone dovrebbe eseguire una copia superficiale. Se l'oggetto fornisce anche una copia approfondita, è possibile utilizzare un Clone sovraccarico (profondità bool).

EDIT: Pattern che uso per "clonare" un oggetto, sta passando un prototipo nel costruttore.

class C
{
  public C ( C prototype )
  {
    ...
  }
}

Ciò rimuove qualsiasi potenziale situazione di implementazione del codice ridondante. A proposito, parlando dei limiti di ICloneable, non sta davvero all'oggetto stesso decidere se eseguire un clone superficiale o profondo, o anche un clone parzialmente superficiale / parzialmente profondo? Dovremmo davvero preoccuparci, purché l'oggetto funzioni come previsto? In alcune occasioni, una buona implementazione di Clone potrebbe benissimo includere sia la clonazione superficiale che quella profonda.


"cattivo" non significa deprecato. Significa semplicemente "stai meglio non usarlo". Non è deprecato nel senso che "rimuoveremo l'interfaccia ICloneable in futuro". Ti incoraggiano semplicemente a non usarlo e non lo aggiorneranno con generici o altre nuove funzionalità.
jalf

Dennis: vedi le risposte di Marc. Problemi di covarianza / ereditarietà rendono questa interfaccia praticamente inutilizzabile per qualsiasi scenario che sia almeno marginalmente complicato.
Tamas Czinege,

Sì, posso vedere i limiti di ICloneable, di sicuro ... Tuttavia, raramente ho bisogno di usare la clonazione.
baretta,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.