2009-07-29 17 views
164

Sé que == tiene algunos problemas al comparar dos Strings. Parece que String.equals() es un mejor enfoque. Bueno, estoy haciendo pruebas JUnit y mi inclinación es usar assertEquals(str1, str2). ¿Es esta una manera confiable de afirmar que dos cadenas contienen el mismo contenido? Usaría assertTrue(str1.equals(str2)), pero luego no obtiene el beneficio de ver cuáles son los valores esperados y reales en caso de falla.Java: ¿Es confiable assertEquals (String, String)?

En una nota relacionada, ¿alguien tiene un enlace a una página o hilo que explica claramente los problemas con str1 == str2?

+1

Si no está seguro, puede leer el código o el Javadoc. Por cierto, si quiere probar que son el mismo objeto, puede usar assertSame. –

+2

Si str1 y str2 son nulos, assertEquals() es verdadero, pero assertTrue (str1.equals (str2)) arroja una excepción. El primer ejemplo también imprimirá un mensaje de error útil como los contenidos de str1 y str2, el segundo no. –

Respuesta

226

Usted debe siempre uso .equals() al comparar Strings en Java.

JUnit llama al método .equals() para determinar la igualdad en el método assertEquals(Object o1, Object o2).

Por lo tanto, definitivamente está seguro usando assertEquals(string1, string2). (porque String s son Object s)

Here is a link to a great Stackoverflow question con respecto a algunas de las diferencias entre == y .equals().

+11

IIRC assertEquals() tiene éxito si ambas cadenas son nulas. Si esto no es lo que quiere, llame también a assertNotNull(). – finnw

+0

@finnw, verificando la fuente, de hecho eres correcto. –

+10

adicionalmente, si quiere probar ==, puede llamar a assertSame() – james

3

Sí, se utiliza todo el tiempo para la prueba. Es muy probable que el marco de prueba use .equals() para comparaciones como estas.

A continuación se muestra un enlace que explica el "error de igualdad de cadenas". Básicamente, las cadenas en Java son objetos, y cuando se compara la igualdad de objetos, generalmente se comparan en función de la dirección de la memoria y no del contenido. Debido a esto, dos cadenas no ocuparán la misma dirección, incluso si su contenido es idéntico, por lo que no coincidirán correctamente, aunque tengan el mismo aspecto cuando se impriman.

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

3
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); 
    } 
} 
2

El JUnit assertEquals(obj1, obj2) en efecto llamar obj1.equals(obj2).

También hay assertSame(obj1, obj2) cual hace obj1 == obj2 (es decir, verifica que obj1 y obj2 se hace referencia a la instancia misma ), que es lo que está tratando de evitar.

Así que estás bien.

4

En resumen: puede tener dos objetos String que contengan los mismos caracteres pero que sean objetos diferentes (en diferentes ubicaciones de memoria). El operador == comprueba que dos referencias apuntan al mismo objeto (ubicación de la memoria), pero el método equals() comprueba si los caracteres son iguales.

Por lo general, le interesa comprobar si dos cadenas contienen los mismos caracteres, no si apuntan a la misma ubicación de memoria.

+0

Pulgares arriba, exactamente el valor de la cadena es lo que quiere comparar o el valor de la ubicación de la memoria – Pomagranite

26

assertEquals usa el método equals para comparar. Hay una afirmación diferente, assertSame, que utiliza el operador ==.

Para comprender por qué == no debe usarse con cadenas, necesita comprender lo que hace ==: realiza una comprobación de identidad. Es decir, a == b comprueba si a y b se refieren al mismo objeto. Está integrado en el lenguaje y su comportamiento no puede ser modificado por diferentes clases. El método equals, por otro lado, puede ser anulado por clases. Si bien su comportamiento predeterminado (en la clase Object) es realizar una comprobación de identidad con el operador ==, muchas clases, incluido String, lo reemplazan para realizar una comprobación de "equivalencia" en su lugar. En el caso de String, en lugar de verificar si a y b se refieren al mismo objeto, a.equals(b) comprueba si los objetos a los que hacen referencia son cadenas que contienen exactamente los mismos caracteres.

Tiempo de analogía: imagine que cada objeto String es un trozo de papel con algo escrito en él. Digamos que tengo dos pedazos de papel con "Foo" escrito en ellos, y otro con "Barra" escrito en él. Si tomo las dos primeras hojas de papel y uso == para compararlas, devolverá false porque básicamente se está preguntando "¿son estas la misma hoja de papel?". No necesita ni siquiera mirar lo que está escrito en el papel. El hecho de que le doy dos hojas de papel (en lugar de la misma dos veces) significa que devolverá false. Si utilizo equals, sin embargo, el método equals leerá los dos trozos de papel y verá que dicen lo mismo ("Foo"), por lo que devolverá true.

El bit que se confunde con Strings es que Java tiene un concepto de "interning" Strings, y esto se realiza (efectivamente) automáticamente en cualquier literal de cadena en su código. Esto significa que si tiene dos literales de cadena equivalentes en su código (incluso si están en clases diferentes), ambos se referirán al mismo objeto String. Esto hace que el operador == devuelva true con más frecuencia de lo que cabría esperar.

+0

"Es decir, a == b comprueba si a y b son el mismo objeto." Técnicamente comprueba si a y b REFEREN al mismo objeto, ya que a y b son referencias. A menos que esté muy equivocado. – andy

+0

@ user1903064 es correcto. Dado que las variables no primitivas solo pueden contener referencias en Java, es común omitir el extra nivel de indirección al hablar de ellos, pero estoy de acuerdo en que en este caso ser más explícito es beneficioso.He actualizado la respuesta. ¡Gracias por la sugerencia! –