Domanda

Questa domanda è specificamente correlata alla sostituzione del metodo equals () per oggetti con un numero elevato di campi. Prima di tutto, lasciami dire che questo grande oggetto non può essere suddiviso in più componenti senza violare i principi OO, quindi dicendomi "nessuna classe dovrebbe avere più di x campi". non aiuterà.

Passando, il problema si è concretizzato quando ho dimenticato di controllare l'uguaglianza di uno dei campi. Pertanto, il mio metodo equals non era corretto. Quindi ho pensato di usare la riflessione:

--code removed because it was too distracting--

Lo scopo di questo post non è necessariamente quello di refactoring del codice (questo non è nemmeno il codice che sto usando), ma piuttosto di ottenere input sul fatto che questa sia una buona idea.

Pro:

  • Se viene aggiunto un nuovo campo, questo viene automaticamente incluso
  • Il metodo è molto più conciso di 30 istruzioni if ??

Contro:

  • Se viene aggiunto un nuovo campo, questo viene automaticamente incluso, a volte ciò è indesiderabile
  • Prestazioni: deve essere più lento, non sento il bisogno di sfondare un profiler
  • La whitelisting di alcuni campi da ignorare nel confronto è un po 'brutta

Qualche idea?

È stato utile?

Soluzione

Se si desidera inserire nella whitelist per motivi di prestazioni, considerare l'utilizzo di un'annotazione per indicare quali campi confrontare. Inoltre, questa implementazione non funzionerà se i tuoi campi non hanno buone implementazioni per equals () .

P.S. Se segui questa strada per equals () , non dimenticare di fare qualcosa di simile per hashCode () .

P.P.S. Confido che tu abbia già considerato HashCodeBuilder e EqualsBuilder .

Altri suggerimenti

Usa Eclipse, FFS!

Elimina l'hashCode ed è uguale ai metodi che hai.

Fai clic destro sul file.

Seleziona Source- > Genera hashcode ed è uguale a ...

Fatto! Non dovrai più preoccuparti della riflessione.

Ripeti per ogni campo aggiunto, basta usare la vista struttura per eliminare i due metodi, quindi lasciare che Eclipse li generi automaticamente.

Se segui l'approccio della riflessione, EqualsBuilder è ancora tuo amico:

 public boolean equals(Object obj) {
    return EqualsBuilder.reflectionEquals(this, obj);
 }

Ecco un pensiero se sei preoccupato per:

1 / Dimenticando di aggiornare la tua grande serie di istruzioni if ??per verificare l'uguaglianza quando aggiungi / rimuovi un campo.

2 / Le prestazioni di farlo nel metodo equals ().

Prova quanto segue:

a / Ripristina l'utilizzo della lunga sequenza di istruzioni if ??nel tuo metodo equals ().

b / Avere una singola funzione che contiene un elenco dei campi (in un array String) e che controllerà tale elenco rispetto alla realtà (cioè i campi riflessi). Genererà un'eccezione se non corrispondono.

c / Nel costruttore per questo oggetto, avere una chiamata run-once sincronizzata a questa funzione (simile a un modello singleton). In altre parole, se questo è il primo oggetto costruito da questa classe, chiama la funzione di controllo descritta in (b) sopra.

L'eccezione renderà immediatamente ovvio quando si esegue il programma se non si sono aggiornate le istruzioni if ??in modo che corrispondano ai campi riflessi; quindi correggi le istruzioni if ??e aggiorna l'elenco dei campi da (b) sopra.

La successiva costruzione di oggetti non eseguirà questo controllo e il metodo equals () verrà eseguito alla massima velocità possibile.

Prova come potrei, non sono stato in grado di trovare problemi reali con questo approccio (potrebbero esserci menti maggiori su StackOverflow) - c'è un controllo delle condizioni extra su ogni costruzione di oggetti per il comportamento run-once ma sembra abbastanza minore.

Se ci provi abbastanza, potresti comunque tenere le tue istruzioni if ??fuori passo con l'elenco dei campi e i campi riflessi, ma l'eccezione assicurerà che l'elenco dei campi corrisponda ai campi riflessi e ti assicuri solo di aggiornare l'if- istruzioni ed elenco dei campi allo stesso tempo.

Puoi sempre annotare i campi che vuoi / non vuoi nel tuo metodo uguale, che dovrebbe essere una modifica semplice e diretta ad esso.

Le prestazioni sono ovviamente correlate alla frequenza con cui l'oggetto viene effettivamente confrontato, ma molti framework usano mappe hash, quindi i tuoi uguali possono essere usati più di quanto pensi.

Inoltre, parlando di mappe hash, hai lo stesso problema con il metodo hashCode.

Infine, hai davvero bisogno di confrontare tutti i campi per l'uguaglianza?

Hai alcuni bug nel tuo codice.

  1. Non puoi presumere che this e obj siano la stessa classe. In effetti, è esplicitamente consentito che obj sia qualsiasi altra classe. Puoi iniziare con se (! Obj instanceof myClass) restituisce false; tuttavia questo è non è ancora corretto perché obj potrebbe essere una sottoclasse di questo con campi aggiuntivi che potrebbero interessare.
  2. Devi supportare i valori null per obj con un semplice se (obj == null) restituisce false;
  3. Non puoi considerare null e stringa vuota uguali. Invece tratta null specialmente. Il modo più semplice qui è iniziare confrontando Field.get (obj) == Field.get (questo) . Se sono entrambi uguali o entrambi indicano lo stesso oggetto, questo è veloce. (Nota: questa è anche un'ottimizzazione, di cui hai bisogno dato che si tratta di una routine lenta.) Se fallisce, puoi usare il veloce se (Field.get (obj) == null || Field.get (questo ) == null) restituisce false; per gestire i casi in cui esattamente uno è null . Finalmente puoi usare il solito equals () .
  4. Non stai utilizzando foundMismatch

Sono d'accordo con Hank che [HashCodeBuilder] [1] e [EqualsBuilder] [2] è un modo migliore di procedere. È facile da mantenere, non molto codice boilerplate, ed eviti tutti questi problemi.

È possibile utilizzare le annotazioni per escludere i campi dal segno di spunta

per es.

@IgnoreEquals
String fieldThatShouldNotBeCompared;

E poi, naturalmente, controlli la presenza dell'annotazione nel tuo metodo di uguaglianza generica.

Se hai accesso ai nomi dei campi, perché non rendi standard i campi che non vuoi includere, inizia sempre con " local " o "nochk" o qualcosa del genere.

Quindi inserisci nella blacklist tutti i campi che iniziano con questo (il codice non è poi così brutto).

Non dubito che sia un po 'più lento. È necessario decidere se si desidera scambiare la facilità di aggiornamento con la velocità di esecuzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top