Яка різниця між == та .equals у Scala?


144

У чому різниця між Scala ==і .equals()в якому, і коли його використовувати?

Чи реалізація така, як у Java?

EDIT: Пов'язане питання стосується конкретних випадків AnyVal. Більш загальний випадок Any.



@Ben Я думаю, що інше питання має бути позначене як дублікат з огляду на задану дату. Також я вважаю, що два питання різні.
Jus12

Відповіді:


201

Ви зазвичай використовуєте ==, він спрямовується до equals, за винятком того, що він nullналежним чином поводиться з. Довідкова рівність (рідко використовується) є eq.


12
Чи застосовується це також при використанні бібліотек Java?
Jus12

20
Це робить. Наприклад, новий java.util.ArrayList [Int] () == новий java.util.ArrayList [Int] (), рівний для ArrayList - це рівність вмісту.
Didier Dupont

5
Існує також якась дивна поведінка навколо Int і Long і == проти .equals (). Те саме число, що і Int і як Long return, істина = =, але false для рівних. Тож == не завжди прямує до рівних.
Гарольд Л

24
Що цікавіше, 3 == BigInt(3)і те BigInt(3) == 3, і інше . Але, 3.equals(BigInt(3))помилково, тоді як BigInt(3).equals(3)це правда. Тому віддайте перевагу використанню ==. Уникайте використання equals()в масштабі. Я думаю, що ==це неявна конверсія добре, але equals()ні.
Naetmul

То чому new java.lang.Integer(1) == new java.lang.Double(1.0)правда, а new java.lang.Integer(1) equals new java.lang.Double(1.0)неправда?
Еастсун

34

==є остаточним методом і викликів .equals, який не є остаточним.

Це кардинально відрізняється від Java, де ==швидше оператор, ніж метод і суворо порівнює еталонну рівність для об'єктів.


29

TL; DR

  • equalsМетод заміщення для порівняння вмісту кожного екземпляра. Це той самий equalsметод, який використовується в Java
  • Використовуйте ==оператор для порівняння, не турбуючись про nullпосилання
  • Використовуйте eqметод, щоб перевірити, чи обидва аргументи ТОЧНО однакові. Рекомендується не використовувати, якщо ви не розумієте, як це працює, і часто equalsбуде працювати для того, що вам потрібно. І переконайтеся, що використовуйте це лише з AnyRefаргументами, а не простоAny

ПРИМІТКА. У випадку equals, як і в Java, він може не повернути той самий результат, якщо ви переключите аргументи, наприклад 1.equals(BigInt(1)), повернеться falseтам, де повернеться обернена true. Це відбувається тому, що кожна реалізація перевіряє лише конкретні типи. Примітивні числа не перевіряють, чи є другий аргумент Numberні BigIntтипу, а лише інших примітивних типів

Деталі

AnyRef.equals(Any)Метод є одним перевизначений підкласами. Метод із специфікації Java, який також потрапив до Scala. Якщо використовується у некомплектному екземплярі, це називається вікном (хоча це приховано в Scala; більш очевидно в Java з int-> Integer). Реалізація за замовчуванням лише порівнює посилання (як у Java)

Any.==(Any)Метод порівнює два об'єкти і дозволяє або аргумент бути порожнім (як якби виклик статичного методу з двома екземплярами). Він порівнює, якщо обидва є null, тоді він викликає equals(Any)метод у коробці.

AnyRef.eq(AnyRef)Метод порівнює тільки посилання, тобто , де примірник знаходиться в пам'яті. Немає неявного боксу для цього методу.

Приклади

  • 1 equals 2повернеться false, як переспрямуєтьсяInteger.equals(...)
  • 1 == 2повернеться false, як переспрямуєтьсяInteger.equals(...)
  • 1 eq 2 не компілюється, оскільки для цього потрібні обидва аргументи AnyRef
  • new ArrayList() equals new ArrayList()повернеться trueпід час перевірки вмісту
  • new ArrayList() == new ArrayList()повернеться true, як переспрямуєтьсяequals(...)
  • new ArrayList() eq new ArrayList()повернеться false, оскільки обидва аргументи - це різні випадки
  • foo equals fooповернеться true, якщо не fooбуде null, то кине аNullPointerException
  • foo == fooповернеться true, навіть якщо fooєnull
  • foo eq fooповернеться true, оскільки обидва аргументи посилаються на одне посилання

6

Існує цікава різниця між ==і equalsдля Floatта Doubleтипами: вони трактуються по- NaNрізному:

scala> Double.NaN == Double.NaN
res3: Boolean = false

scala> Double.NaN equals Double.NaN
res4: Boolean = true

Редагувати: Як було зазначено в коментарі - "це теж відбувається на Java" - залежить від того, що саме це :

public static void main(final String... args) {
    final double unboxedNaN = Double.NaN;
    final Double boxedNaN = Double.valueOf(Double.NaN);

    System.out.println(unboxedNaN == unboxedNaN);
    System.out.println(boxedNaN == boxedNaN);
    System.out.println(boxedNaN.equals(boxedNaN));
}

Це надрукується

false
true
true

Таким чином, unboxedNanдохідність falseпорівнюється за рівність, тому що саме так визначають номери плаваючої точки IEEE, і це дійсно має відбуватися в кожній мові програмування (хоча це якось псується з поняттям ідентичності).

Позначений в коробці NaN відповідає справжньому для порівняння, використовуючи ==в Java, порівнюючи посилання на об'єкти.

У мене немає пояснення для цього equalsвипадку, IMHO він дійсно повинен поводитись так само, як і ==у необов’язаних подвійних значеннях, але це не так.

У перекладі на Scala справа є дещо складнішою, оскільки Scala об'єднав примітивні та об'єктні типи в Anyта перекладає на примітивний подвійний та в коробковий подвійний, якщо це потрібно. Таким чином, масштаб, ==очевидно, зводиться до порівняння примітивних NaNзначень, але equalsвикористовує одне, визначене в коробкових подвійних значеннях (відбувається безліч неявних магічних перетворень, і речі, прикріплені до подвоєнь RichDouble).

Якщо вам дійсно потрібно з’ясувати, чи щось насправді NaNвикористовується isNaN:


і це теж відбувається на Java!
Іван Сатрія

4

У Scala == спочатку перевіряють значення Null, а потім викликує метод рівняння на першому об'єкті

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