Является ли метод 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.