Чи надійний метод assortEquals Java?


199

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

Чи є у когось пов’язана примітка, чи є у когось посилання на сторінку чи потік, що чітко пояснює проблеми str1 == str2?


1
Якщо ви не впевнені, можете прочитати код або Javadoc. BTW, якщо ви хочете перевірити, що вони є одним і тим же об'єктом, ви можете використовувати assertSame.
Пітер Лорі

2
Якщо str1 і str2 є null, assertEquals () вірно, але assertTrue (str1.equals (str2)) кидає виняток. Перший приклад також надрукує корисне повідомлення про помилку, таке як вміст str1 та str2, другий - ні.
Пітер Лорі

Відповіді:


274

Ви завжди повинні використовувати .equals()при порівнянні Stringsна Java.

JUnit називає .equals()метод визначення рівності в методі assertEquals(Object o1, Object o2).

Отже, ви точно користуєтеся безпекою assertEquals(string1, string2). (Тому Stringщо Objects)

Ось посилання на чудове питання Stackoverflow щодо деяких відмінностей між ==та .equals().


12
IIRC assertEquals () вдається, якщо обидва рядки є нульовими. Якщо це не те, що ви хочете, тоді також зателефонуйте assertNotNull ().
finnw

10
крім того, якщо ви хочете протестувати на ==, ви можете зателефонувати на assrtSame ()
Джеймс

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.

Біт, який заплутається у Strings, полягає в тому, що Java має концепцію "інтернування" Strings, і це (ефективно) автоматично виконується на будь-яких рядкових літералах вашого коду. Це означає, що якщо у вашому коді є два еквівалентні рядкові літерали (навіть якщо вони є в різних класах), вони насправді обидва посилаються на один і той же 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/12stringcomppare.html

Stringє Objectin java, тому він підпадає під категорію правил порівняння.


Це не дало відповіді на запитання і є свого роду оманливим. Ви не можете надійно виконати == на рядку
CodeMonkey
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.