В чем разница между == и .equals в Scala?

150

В чем разница между ==и .equals()в Scala и когда какой использовать?

Реализация такая же, как в Java?

РЕДАКТИРОВАТЬ: в соответствующем вопросе говорится о конкретных случаях AnyVal. Более общий случай Any.

Jus12
источник
@Ben Я думаю, что другой вопрос следует пометить как повторяющийся, учитывая заданную дату. Кроме того, я считаю, что это разные вопросы.
Jus12 07

Ответы:

205

Обычно вы используете ==, он направляет equals, за исключением того, что обрабатывает nulls правильно. Ссылочное равенство (используется редко) есть eq.

Дидье Дюпон
источник
12
Применяется ли это также при использовании библиотек Java?
Jus12 06
20
Оно делает. Например, new java.util.ArrayList [Int] () == new java.util.ArrayList [Int] (), равным в ArrayList является равенство содержимого.
Дидье Дюпон
5
Также есть странное поведение с Int и Long и == по сравнению с .equals (). То же число, что и Int и Long, возвращают true для ==, но false для equals. Итак, == не всегда направляет на равные.
Harold L
24
Что еще интереснее, оба 3 == BigInt(3)и BigInt(3) == 3верны. Но 3.equals(BigInt(3))это ложь, тогда BigInt(3).equals(3)как правда. Поэтому предпочитаю использовать ==. Избегайте использования equals()в scala. Я думаю, ==что неявное преобразование хорошо, но 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)ложь?
Eastsun
37

==это последний метод и вызывает .equals, который не является окончательным.

Это радикально отличается от Java, где ==оператор, а не метод, строго сравнивает ссылочное равенство для объектов.

Дон Роби
источник
31

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 (как при вызове статического метода с двумя экземплярами). Он сравнивает, если оба они есть 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, поскольку оба аргумента ссылаются на одну и ту же ссылку
zjohn4
источник
Можете ли вы также объяснить === в scala?
user2441441
6

Существует интересная разница между типами ==и equalsfor 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 в рамке дает значение true для сравнения, которое используется ==в Java, поскольку мы сравниваем ссылки на объекты.

У меня нет объяснения этому equalsслучаю, ИМХО, он действительно должен вести себя так же, как и ==с неупакованными двойными значениями, но это не так.

В переводе на Scala дело обстоит немного сложнее, так как Scala объединила примитивные и объектные типы в Anyпримитивный двойной и упакованный в штучный двойной тип и при необходимости преобразует их. Таким образом, scala, по- ==видимому, сводится к сравнению примитивных NaNзначений, но equalsиспользует тот, который определен для значений Double в штучной упаковке (происходит много неявной магии преобразования, и есть вещи, которые накладываются на двойные значения RichDouble).

Если вам действительно нужно узнать, действительно ли что-то NaNиспользуется isNaN:

царапина
источник
и это тоже происходит в Java!
Iwan Satria
4

В Scala == сначала проверьте значения Null, а затем вызовите метод equals для первого объекта

Джек
источник