2009-04-26 10 views
6

Nel mio progetto Java, ho un vettore di vari tipi di Commercianti. Questi diversi tipi di trader sono sottoclassi della classe Trader. In questo momento, ho un metodo che accetta un Trader come argomento e lo memorizza circa 50 volte nel vettore. Sto avendo problemi perché la memorizzazione dello stesso oggetto 50 volte è solo la memorizzazione di 50 riferimenti dello stesso oggetto. Devo memorizzare 50 copie dell'oggetto. Ho studiato l'implementazione di Clone, ma non voglio che i programmatori definiscano un tipo di Trader che deve preoccuparsi di rendere la propria classe clonabile. Inoltre, come sottolineato da this page, l'implementazione del clone crea tutti i tipi di problemi. Non penso che un costruttore di copia possa funzionare o perché se ne definissi uno nella classe Trader, non saprebbe il tipo di Trader che stava copiando e basta creare un Trader generico. Cosa posso fare?Esistono alternative all'implementazione di Clone in Java?

Edit: Non sto davvero volendo fare copie esatte di un certo oggetto. Quello che sto cercando di fare è aggiungere un certo numero di trader al vettore. Il problema è che l'utente deve specificare in un argomento quale tipo di commerciante vuole aggiungere. Ecco un esempio di quello che sto cercando di fare: (anche se la mia sintassi è completamente immaginario)

public void addTraders(*traderType*) 
{ 
    tradervect.add(new *traderType*()) 
} 

Come posso ottenere qualcosa di simile a Java?

risposta

2

basta aggiungere un metodo di copia astratto. È possibile utilizzare i tipi di restituzione covarianti in modo che venga specificato il tipo derivato per restituire un'istanza derivata, che può essere importante o meno.

public interface Trader { 
    Trader copyTrader(); 
    ... 
} 


public final class MyTrader implements Trader { 
    MyTrader copyTrader() { 
     return new MyTrader(this); 
    } 
    ... 
} 

A volte si potrebbe desiderare di generico accordo con una collezione di tipo derivato di Trader che ha bisogno di clonare e quindi restituire il una raccolta correttamente digitato. Per questo è possibile utilizzare farmaci generici in modo idiomatico:

public interface Trader<THIS extends Trader> { 
    THIS copyTrader(); 
    ... 
} 


public final class MyTrader implements Trader<MyTrader> { 
    public MyTrader copyTrader() { 
     return new MyTrader(this); 
    } 
    ... 
} 
+0

Non vedo come posso rendere la classe Trader generica un'interfaccia perché la classe Trader ha metodi che devono essere accessibili. Fare un'interfaccia significherebbe che tutti quei metodi di basso livello non permetterebbero di definire quei metodi nella classe Trader. –

+0

Se Trader è una classe astratta o un'interfaccia è irrilevante. Generalmente un'interfaccia è preferita su una classe astratta. Preferisci anche la delega all'eredità. –

0

Un'opzione: se è possibile rendere serializzabili gli oggetti, è possibile serializzarli e deserializzarli per creare una copia, simile a ciò che accade quando si passa un oggetto su RMI.

metodo di copia rapida:

public MyObject copy() { 
    ObjectOutputStream oos = null; 
    ObjectInputStream ois = null; 
    try { 
     ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     oos = new ObjectOutputStream(bos); 
     oos.writeObject(this); 
     oos.flush(); 
     ByteArrayInputStream bin = 
      new ByteArrayInputStream(bos.toByteArray()); 
     ois = new ObjectInputStream(bin); 
     return (MyObject)ois.readObject(); 
    } catch(Exception e) { 
     return null; 
    } finally { 
     try { 
      oos.close(); 
      ois.close(); 
     } catch (Exception e) { 
      return null; 
     } 
    } 
} 
+0

Perché il downvote? È un modo adeguato di rappresentare un oggetto complesso. La gestione delle risorse è completamente e completamente rotta però. –

+0

È questa l'unica soluzione? Non riesco a pensare che un'operazione così semplice possa causare tanto clamore. Cosa succede se implementerei Clone per consentire la copia. Sarebbe anche possibile? –

+0

@Tom: Non sono sicuro, ma l'utilizzo della serializzazione/deserializzazione per la clonazione è tanto sottile quanto l'uso di memcpy invece dei costruttori di copia in C++. – Uri

-2

Un modo è quello di rendere una classe finale come propria stringa di Java, che renderà qualsiasi modifica a un oggetto della classe Trader per creare una nuova copia in memoria , ma renderà impossibile la sua sottoclasse.

Un altro (migliore) metodo consiste nell'utilizzare un metodo factory per creare e copiare gli oggetti di Trader, il che implica che non è necessario consentire l'utilizzo del costruttore predefinito, ovvero renderlo privato. In questo modo puoi controllare il numero di istanze della classe. Vedi http://en.wikipedia.org/wiki/Factory_method

public class Trader { 

    /* prevent constructor so new cant be used outside the factory method */ 
    private Trader() { 
    } 

    /* the factory method */ 
    public static Trader createTrader(int whatKindOfTrader) { 

     switch (whatKindOfTrader) { 
     case 0: 
      return new Trader1(); // extends Trader 
     case 1: 
     default: 
      return new Trader2(); // extends Trader 
     } 
     return new Trader3(); // or throw exception 
    } 
} 

Si potrebbe anche specificare un altro metodo di overload, o di un secondo argomento che richiede un commerciante e lo copia in uno nuovo, sostituendo così clone. Btw, potresti voler sovrascrivere il metodo clone() e lanciare CloneNotSupportedException, per impedire la clonazione dell'oggetto predefinita.

+0

Intendi immutabile anziché solo una lezione finale. L'immutabilità probabilmente sta andando un po 'lontano per un oggetto "commerciante". –

+0

Non ho immutato, ma è definitivo. C'è una differenza, e personalmente non lo userei mai. Ma il metodo Factory è uno dei migliori, ed è un modello di progettazione comune Btw, l'oggetto immutabile non è utile in questo caso. – Azder

2

Sono un po 'incerto sul motivo per cui si desidera archiviare 50 o più cloni dello stesso oggetto a meno che il trader originale serva da prototipo (vedere lo schema) per i trader successivi.

Se si desidera creare una copia esatta di un oggetto, è necessario tenere conto del problema del polimorfismo. Se le persone che sottoclassi la tua classe data sono autorizzati ad aggiungere membri dello stato, allora hai già abbastanza mal di testa con funzioni come uguali e compare, quel clone è ancora un caso in cui è necessario richiedere una gestione speciale.

Non sono d'accordo sul fatto che il clone sia sempre malvagio, a volte è necessario. Tuttavia, in situazioni di sottoclasse, molte cose diventano complicate.

Ti consiglio di leggere (quando ne hai la possibilità) "Effective Java" di Bloch, che copre molti dei suoi argomenti. Il punto di vista di Bracha è che non è una buona idea permettere agli altri di estendere le lezioni e che, se lo fai, devi documentare molto bene cosa dovrebbero fare e sperare che seguano le tue istruzioni. Non c'è davvero un modo per aggirare questo. Potresti anche voler rendere immutabili i tuoi Traders.

Procedere normalmente ad uno strumento Clona e indicare in modo molto chiaro nell'intestazione della classe che chiunque erediti dal proprio Commerciante e aggiunge membri dello stato deve implementare i metodi X (specificare quale).

Trovare trucchi per bypassare un Clonabile reale e ancora Clone non supererà il problema dell'ereditarietà. Non c'è nessun proiettile d'argento qui.

+0

La tua risposta mi ha fatto riflettere. Vedi la modifica che ho fatto per maggiori informazioni.:) –

+0

Aspetta, stai essenzialmente dicendo che se X estende Trader e qualcuno ha passato un'istanza di X alla tua funzione, vuoi ottenere un'altra nuova X? – Uri

+0

Se si desidera che questo comportamento, si utilizza la (orribile) getClass.newInstance linguaggio:. traderType.getClass() newInstance() Bisogna fare in modo che ci sia sempre un costruttore pubblico, e probabilmente vuole un po 'di init metodo che puoi sempre chiamare per portarlo in uno stato legittimo. – Uri

0

Uri ha ragione, il polimorfismo con stato apre una grande scatola di vermi.

Penso che la sottoclasse Clone clonabile e sovrascrivente() sia probabilmente il modo più semplice. Puoi, credo, rendere covariante il tipo di ritorno.