Il metodo assertEquals di Java è affidabile?


199

So che ==ha alcuni problemi quando si confrontano due Strings. Sembra che String.equals()sia un approccio migliore. Bene, sto facendo i test JUnit e la mia inclinazione è usare assertEquals(str1, str2). È un modo affidabile per affermare che due stringhe contengono lo stesso contenuto? Vorrei usare assertTrue(str1.equals(str2)), ma poi non si ottiene il vantaggio di vedere quali valori attesi e reali sono in errore.

In una nota correlata, qualcuno ha un link a una pagina o thread che spiega chiaramente i problemi con str1 == str2?


1
Se non sei sicuro, puoi leggere il codice o Javadoc. A proposito, se si desidera verificare che siano lo stesso oggetto, è possibile utilizzare assertSame.
Peter Lawrey,

2
Se str1 e str2 sono null, assertEquals () è true, ma assertTrue (str1.equals (str2)) genera un'eccezione. Il primo esempio stamperà anche un utile messaggio di errore come il contenuto di str1 e str2, il secondo no.
Peter Lawrey,

Risposte:


274

Si dovrebbe sempre usare .equals()quando si confronta Stringsin Java.

JUnit chiama il .equals()metodo per determinare l'uguaglianza nel metodo assertEquals(Object o1, Object o2).

Quindi, sei decisamente sicuro usando assertEquals(string1, string2). (Perché Strings sono Objects)

Ecco un link a una grande domanda StackOverflow riguardante alcune delle differenze tra ==e .equals().


12
IIRC assertEquals () ha esito positivo se entrambe le stringhe sono null. Se questo non è ciò che desideri, chiama anche assertNotNull ().
Finnw,

10
Inoltre, se si desidera verificare ==, è possibile chiamare assertSame ()
james

7
Non direi sempre ; talvolta si desidera l'uguaglianza di riferimento, anche per le stringhe.
Karu,

30

assertEqualsutilizza il equalsmetodo per il confronto. C'è un'asserzione diversa assertSame, che utilizza l' ==operatore.

Per capire perché ==non dovrebbe essere usato con le stringhe devi capire cosa ==fa: fa un controllo di identità. Cioè, a == bcontrolla se ae fa briferimento allo stesso oggetto . È incorporato nella lingua e il suo comportamento non può essere modificato da classi diverse. Il equalsmetodo, d'altra parte, può essere sovrascritto dalle classi. Mentre il suo comportamento predefinito (nella Objectclasse) è quello di eseguire un controllo di identità utilizzando l' ==operatore, molte classi, incluso String, lo sovrascrivono invece di eseguire un controllo di "equivalenza". Nel caso di String, invece di verificare se ae bfare riferimento allo stesso oggetto,a.equals(b) controlla se gli oggetti a cui fanno riferimento sono entrambe stringhe che contengono esattamente gli stessi caratteri.

Tempo di analogia: immagina che ogni Stringoggetto sia un pezzo di carta con sopra scritto qualcosa. Diciamo che ho due pezzi di carta con "Foo" scritto su di essi e un altro con "Bar" scritto su di esso. Se prendo i primi due pezzi di carta e li uso ==per confrontarli, tornerà falseperché essenzialmente chiede "sono questi gli stessi pezzi di carta?". Non ha nemmeno bisogno di guardare ciò che è scritto sul foglio. Il fatto che gli do due pezzi di carta (anziché due volte lo stesso) significa che tornerà false. Se uso equals, tuttavia, il equalsmetodo leggerà i due pezzi di carta e vedrà che dicono la stessa cosa ("Foo"), e quindi tornerà true.

Il bit che confonde con Strings è che Java ha un concetto di "interning" Strings, e questo viene (effettivamente) automaticamente eseguito su qualsiasi letterale di stringa nel tuo codice. Ciò significa che se nel tuo codice sono presenti due valori letterali stringa equivalenti (anche se si trovano in classi diverse), entrambi faranno effettivamente riferimento allo stesso Stringoggetto. Questo fa ==tornare l' operatore truepiù spesso di quanto ci si potrebbe aspettare.


"Cioè, a == b controlla se aeb sono lo stesso oggetto." Tecnicamente controlla se aeb si riferisce allo stesso oggetto, poiché aeb sono riferimenti. A meno che non mi sbagli di grosso.
bob

@ user1903064 che è corretto. Poiché le variabili non primitive possono contenere solo riferimenti in Java, è comune saltare il livello extra di indiretto quando si parla di esse, ma sono d'accordo sul fatto che in questo caso essere più espliciti è vantaggioso. Ho aggiornato la risposta. Grazie per il suggerimento!
Laurence Gonsalves il

7

In poche parole: puoi avere due oggetti String che contengono gli stessi caratteri ma sono oggetti diversi (in posizioni di memoria diverse). L'operatore == verifica che due riferimenti puntino allo stesso oggetto (posizione di memoria), ma il metodo equals () controlla se i caratteri sono uguali.

Di solito sei interessato a verificare se due stringhe contengono gli stessi caratteri, non se indicano la stessa posizione di memoria.


4
public class StringEqualityTest extends TestCase {
    public void testEquality() throws Exception {
        String a = "abcde";
        String b = new String(a);
        assertTrue(a.equals(b));
        assertFalse(a == b);
        assertEquals(a, b);
    }
}

3

Sì, viene utilizzato continuamente per i test. È molto probabile che il framework di test utilizzi .equals () per confronti come questi.

Di seguito è riportato un link che spiega "l'errore di uguaglianza delle stringhe". In sostanza, le stringhe in Java sono oggetti e quando si confronta l'uguaglianza degli oggetti, in genere vengono confrontate in base all'indirizzo di memoria e non in base al contenuto. Per questo motivo, due stringhe non occuperanno lo stesso indirizzo, anche se il loro contenuto è identico, quindi non corrisponderanno correttamente, anche se sembrano uguali quando vengono stampate.

http://blog.enrii.com/2006/03/15/java-string-equality-common-mistake/


3

La JUnit assertEquals(obj1, obj2)chiama davvero obj1.equals(obj2).

C'è anche assertSame(obj1, obj2)ciò che obj1 == obj2(cioè, lo verifica obj1e obj2si riferisce alla stessa istanza), che è ciò che stai cercando di evitare.

Quindi stai bene.


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.