Является ли метод Java assertEquals надежным?


199

Я знаю, что ==есть некоторые проблемы при сравнении двух Strings. Кажется, String.equals()это лучший подход. Ну, я делаю тестирование JUnit, и я склонен использовать assertEquals(str1, str2). Это надежный способ утверждать, что две строки содержат одинаковое содержимое? Я бы использовал assertTrue(str1.equals(str2)), но тогда вы не получите преимущества от просмотра ожидаемых и фактических значений при сбое.

На связанной заметке, есть ли у кого-нибудь ссылка на страницу или ветку, которая явно объясняет проблемы str1 == str2?


1
Если вы не уверены, вы можете прочитать код или Javadoc. Кстати, если вы хотите проверить, что это один и тот же объект, вы можете использовать assertSame.
— Питер Лори

2
Если str1 и str2 равны нулю, assertEquals () имеет значение true, но assertTrue (str1.equals (str2)) выдает исключение. В первом примере также будет напечатано полезное сообщение об ошибке, такое как содержимое str1 и str2, во втором - нет.
— Питер Лори

Ответы:


274

Вы должны всегда использовать .equals()при сравнении Stringsв Java.

JUnit вызывает .equals()метод для определения равенства в методе assertEquals(Object o1, Object o2).

Таким образом, вы, безусловно, безопасно использовать assertEquals(string1, string2). (Потому что Strings есть Objects)

Вот ссылка на отличный вопрос о Stackoverflow, касающийся некоторых различий между ==и .equals().


12
IIRC assertEquals () завершается успешно, если обе строки имеют значение null. Если это не то, что вы хотите, тогда вызовите также assertNotNull ().
— finnw

10
Кроме того, если вы хотите проверить на ==, вы можете вызвать assertSame ()
— Джеймс

7
Я бы не сказал всегда ; иногда ссылочное равенство желательно даже для строк.
— Кару

30

assertEqualsиспользует equalsметод для сравнения. Существует другое утверждение assertSame, которое использует ==оператор.

Чтобы понять, почему ==не следует использовать со строками, вам нужно понять, что ==делает: он проверяет идентичность. То есть a == bпроверяет, есть ли aи bссылаются ли на один и тот же объект . Он встроен в язык, и его поведение не может быть изменено различными классами. equalsМетод, с другой стороны, могут быть перекрыты классами. Хотя его поведение по умолчанию (в Objectклассе) заключается в проверке идентичности с использованием ==оператора, многие классы, в том числе String, переопределяют его, чтобы вместо этого выполнить проверку "эквивалентности". В случае String, вместо проверки, если aи bссылаются на тот же объект,a.equals(b) проверяет, являются ли объекты, на которые они ссылаются, строками, которые содержат одинаковые символы.

Время аналогии: представьте, что каждый Stringпредмет - это лист бумаги с чем-то написанным на нем. Допустим, у меня есть два листа бумаги с надписью «Foo» и еще один с надписью «Bar». Если я возьму первые два листа бумаги и использую их ==для сравнения, он вернется, falseпотому что по сути спрашивает «это один и тот же лист бумаги?». Не нужно даже смотреть на то, что написано на бумаге. Тот факт, что я даю ему два листа бумаги (а не один и тот же дважды), означает, что он вернется false. Если я использую equals, однако, equalsметод прочитает две бумажки и увидит, что они говорят одно и то же («Foo»), и поэтому он вернется true.

Проблема, которая путает со строками, заключается в том, что в Java есть концепция «интернирования» строк, и это (эффективно) автоматически выполняется для любых строковых литералов в вашем коде. Это означает, что если в вашем коде есть два эквивалентных строковых литерала (даже если они находятся в разных классах), они на самом деле оба ссылаются на один и тот же Stringобъект. Это заставляет ==оператора возвращаться trueчаще, чем можно было ожидать.


«То есть a == b проверяет, являются ли a и b одним и тем же объектом». Технически он проверяет, относятся ли a и b к одному и тому же объекту, поскольку a и b являются ссылками. Если я не очень неправ.
— Боб

@ user1903064 это правильно. Поскольку не примитивные переменные могут содержать ссылки только на Java, обычно при их обсуждении пропускают дополнительный уровень косвенности, но я согласен, что в этом случае более явное выражение полезно. Я обновил ответ. Спасибо за предложение!
— Лоуренс Гонсалвес

7

В двух словах - у вас может быть два объекта String, которые содержат одинаковые символы, но являются разными объектами (в разных местах памяти). Оператор == проверяет, что две ссылки указывают на один и тот же объект (область памяти), но метод equals () проверяет, совпадают ли символы.

Обычно вас интересует проверка, содержат ли две строки одинаковые символы, а не указывают ли они на одну и ту же ячейку памяти.


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

Да, он все время используется для тестирования. Весьма вероятно, что среда тестирования использует .equals () для таких сравнений.

Ниже приведена ссылка, объясняющая «ошибку равенства строк». По сути, строки в Java являются объектами, и когда вы сравниваете равенство объектов, обычно они сравниваются по адресу памяти, а не по содержимому. Из-за этого две строки не будут занимать один и тот же адрес, даже если их содержимое идентично, поэтому они не будут соответствовать друг другу, даже если при печати они выглядят одинаково.

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


3

JUnit assertEquals(obj1, obj2)действительно звонит obj1.equals(obj2).

Есть также то, assertSame(obj1, obj2)что делает obj1 == obj2(то есть, проверяет это obj1и obj2ссылается на тот же экземпляр), чего вы пытаетесь избежать.

Так что ты в порядке.


0

« ==Оператор проверяет, Objectsявляются ли два абсолютно одинаковыми Object».

http://leepoint.net/notes-java/data/strings/12stringcomparison.html

Stringнаходится Objectв Java, поэтому он попадает в эту категорию правил сравнения.


Это не ответило на вопрос и вводит в заблуждение. Вы не можете сделать == над строкой надежно
— CodeMonkey
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.