Jaka jest różnica między == i .equals w Scali?

144

Jaka jest różnica między ==i .equals()w Scala, a kiedy używać które?

Czy implementacja jest taka sama jak w Javie?

EDYCJA: Powiązane pytanie dotyczy konkretnych przypadków AnyVal. Bardziej ogólny przypadek to Any.

Jus12
źródło
@Ben Myślę, że inne pytanie powinno być oznaczone jako zduplikowane, biorąc pod uwagę zadaną datę. Czuję też, że te dwa pytania są różne.
Jus12,

Odpowiedzi:

201

Zwykle używasz ==, kieruje do equals, z wyjątkiem tego, że traktuje je nullpoprawnie. Równość odwołań (rzadko używana) to eq.

Didier Dupont
źródło
12
Czy ma to również zastosowanie w przypadku korzystania z bibliotek Java?
Jus12
20
To robi. Na przykład new java.util.ArrayList [Int] () == new java.util.ArrayList [Int] (), as równa się na ArrayList to równość treści.
Didier Dupont
5
Występuje również dziwne zachowanie wokół Int i Long oraz == w porównaniu z .equals (). Ta sama liczba co Int i jako Long zwraca true dla ==, ale false dla równych. Tak więc == nie zawsze prowadzi do równości.
Harold L
24
Co ciekawsze, oba 3 == BigInt(3)i BigInt(3) == 3są prawdziwe. Ale 3.equals(BigInt(3))jest fałszywe, podczas gdy BigInt(3).equals(3)jest prawdziwe. Dlatego wolę używać ==. Unikaj używania equals()w scali. Myślę, że ==niejawna konwersja dobrze equals()działa , ale tak nie jest.
Naetmul
Dlaczego więc new java.lang.Integer(1) == new java.lang.Double(1.0)jest prawdziwe, podczas gdy new java.lang.Integer(1) equals new java.lang.Double(1.0)jest fałszywe?
Eastsun
34

==jest ostateczną metodą, a wywołania .equals, które nie są ostateczne.

Jest to radykalnie inne niż Java, gdzie ==jest operatorem, a nie metodą i ściśle porównuje równość odwołań dla obiektów.

Don Roby
źródło
29

TL; DR

  • Override equals, aby porównać zawartość każdego wystąpienia. To ta sama equalsmetoda, która jest używana w Javie
  • Użyj ==operatora do porównania, nie martwiąc się o nullodwołania
  • Użyj eqmetody, aby sprawdzić, czy oba argumenty są DOKŁADNIE tym samym odwołaniem. Zaleca się, aby nie używać, chyba że rozumiesz, jak to działa i często equalsbędzie działać w przypadku tego, czego potrzebujesz. I upewnij się, że używasz tego tylko z AnyRefargumentami, a nie tylkoAny

UWAGA: W przypadku equals, tak jak w Javie, może nie zwrócić tego samego wyniku, jeśli zmienisz argumenty, np. 1.equals(BigInt(1))Zwróci falsetam, gdzie zwróci odwrotność true. Dzieje się tak, ponieważ każda implementacja sprawdza tylko określone typy. Liczby pierwotne nie sprawdzają, czy drugi argument jest typu Numbernor, BigIntale tylko innych typów pierwotnych

Detale

AnyRef.equals(Any)Metoda jest jedną zastąpione przez podklasy. Metoda ze specyfikacji Java, która również trafiła do Scali. Jeśli jest używany w instancji bez opakowania, jest zaznaczony, aby to wywołać (choć ukryty w Scali; bardziej oczywiste w Javie z int-> Integer). Domyślna implementacja jedynie porównuje referencje (jak w Javie)

Any.==(Any)Sposób porównuje dwa przedmioty i pozwala oba argumenty będą puste (jak wywołanie statyczne metody z dwóch przypadkach). Porównuje, jeśli oba są null, a następnie wywołuje equals(Any)metodę w pudełkowej instancji.

AnyRef.eq(AnyRef)Sposób porównuje tylko odniesienia, to jest, gdy przykład znajduje się w pamięci. W przypadku tej metody nie ma niejawnego boksu.

Przykłady

  • 1 equals 2powróci false, ponieważ przekierowuje doInteger.equals(...)
  • 1 == 2powróci false, ponieważ przekierowuje doInteger.equals(...)
  • 1 eq 2 nie skompiluje się, ponieważ wymaga, aby oba argumenty były typu AnyRef
  • new ArrayList() equals new ArrayList()zwróci true, ponieważ sprawdza zawartość
  • new ArrayList() == new ArrayList()powróci true, ponieważ przekierowuje doequals(...)
  • new ArrayList() eq new ArrayList()zwróci false, ponieważ oba argumenty są różnymi wystąpieniami
  • foo equals foozwróci true, chyba że foojest null, a następnie wyrzuciNullPointerException
  • foo == foopowróci true, nawet jeśli foojestnull
  • foo eq foozwróci wartość true, ponieważ oba argumenty prowadzą do tego samego odwołania
zjohn4
źródło
6

Istnieje interesująca różnica między typami ==i equalsdla Floatoraz Double: traktują one NaNinaczej:

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

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

Edit: Jak wskazano w komentarzu - „tak się dzieje również w Javie” - zależy od tego, co dokładnie to jest:

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));
}

To się wydrukuje

false
true
true

Tak więc unboxedNanwydajność falseporównana pod kątem równości, ponieważ tak definiują ją liczby zmiennoprzecinkowe IEEE i powinno to naprawdę mieć miejsce w każdym języku programowania (chociaż w jakiś sposób miesza się z pojęciem tożsamości).

Pole NaN daje wartość true dla porównania używanego ==w Javie, gdy porównujemy odwołania do obiektów.

Nie mam wytłumaczenia dla equalssprawy, IMHO to naprawdę powinno zachowywać się tak samo jak ==na unboxed double wartości, ale tak nie jest.

W tłumaczeniu na Scala sprawa jest trochę bardziej skomplikowana, ponieważ Scala ujednolicił typy prymitywne i obiektowe na Anyi tłumaczy na prymitywne podwójne i pudełkowe podwójne w razie potrzeby. Tak więc skala ==najwyraźniej sprowadza się do porównania NaNwartości pierwotnych , ale equalswykorzystuje tę zdefiniowaną na opakowanych wartościach Double (dzieje się wiele niejawnej magii konwersji i jest coś podwojonego przez podwójne RichDouble).

Jeśli naprawdę chcesz się dowiedzieć, czy coś jest rzeczywiście NaNużywane isNaN:

straszny
źródło
i dzieje się tak również w Javie!
Iwan Satria
4

W Scala == najpierw sprawdź wartości Null , a następnie wywołuje metodę equals na pierwszym obiekcie

Jacek
źródło