Was ist der Unterschied zwischen == und .equals in Scala?

144

Was ist der Unterschied zwischen ==und .equals()in Scala und wann welche zu verwenden?

Ist die Implementierung dieselbe wie in Java?

EDIT: Die verwandte Frage spricht über bestimmte Fälle von AnyVal. Der allgemeinere Fall ist Any.

Jus12
quelle
@ Ben Ich denke, dass andere Fragen angesichts des Datums als doppelt markiert werden sollten. Ich bin auch der Meinung, dass die beiden Fragen unterschiedlich sind.
Jus12

Antworten:

201

Normalerweise verwendet ==es Routen zu equals, außer dass es nulls richtig behandelt . Referenzgleichheit (selten verwendet) ist eq.

Didier Dupont
quelle
12
Gilt das auch für die Verwendung von Java-Bibliotheken?
Jus12
20
Es tut. Zum Beispiel ist new java.util.ArrayList [Int] () == new java.util.ArrayList [Int] (), da auf ArrayList gleich Inhaltsgleichheit ist.
Didier Dupont
5
Es gibt auch ein seltsames Verhalten bei Int und Long und == versus .equals (). Die gleiche Zahl wie Int und Long gibt true für ==, aber false für equals zurück. Also == routet nicht immer gleich.
Harold L
24
Interessanter ist, dass beide 3 == BigInt(3)und BigInt(3) == 3wahr sind. Aber 3.equals(BigInt(3))ist falsch, während BigInt(3).equals(3)es wahr ist. Verwenden Sie daher lieber ==. Vermeiden Sie die Verwendung equals()in Scala. Ich denke ==, implizite Konvertierung ist gut, aber equals()nicht.
Naetmul
Warum new java.lang.Integer(1) == new java.lang.Double(1.0)ist wahr, während new java.lang.Integer(1) equals new java.lang.Double(1.0)falsch ist?
Eastsun
33

==ist eine endgültige Methode und ruft auf .equals, was nicht endgültig ist.

Dies unterscheidet sich grundlegend von Java, bei dem ==es sich eher um einen Operator als um eine Methode handelt und die Referenzgleichheit für Objekte streng verglichen wird.

Don Roby
quelle
29

TL; DR

  • equalsMethode überschreiben , um den Inhalt jeder Instanz zu vergleichen. Dies ist die gleiche equalsMethode wie in Java
  • Verwenden Sie den ==Operator zum Vergleichen, ohne sich um nullReferenzen kümmern zu müssen
  • Verwenden Sie die eqMethode, um zu überprüfen, ob beide Argumente genau dieselbe Referenz sind. Empfohlen, nicht zu verwenden, es sei denn, Sie verstehen, wie dies funktioniert, und funktionieren häufig equalsfür das, was Sie stattdessen benötigen. Und stellen Sie sicher, dass Sie dies nur mit AnyRefArgumenten verwenden, nicht nurAny

HINWEIS: Im Fall von equals, wie in Java, wird möglicherweise nicht das gleiche Ergebnis zurückgegeben, wenn Sie die Argumente wechseln, z. B. 1.equals(BigInt(1))wird dort zurückgegeben, falsewo die Umkehrung zurückgegeben wird true. Dies liegt daran, dass jede Implementierung nur bestimmte Typen überprüft. Primitive Zahlen prüfen nicht, ob das zweite Argument Numbernoch vom BigIntTyp ist, sondern nur von anderen primitiven Typen

Einzelheiten

Die AnyRef.equals(Any)Methode wird von Unterklassen überschrieben. Eine Methode aus der Java-Spezifikation, die auch für Scala verfügbar ist. Wenn es für eine Instanz ohne Box verwendet wird, wird es als Box aufgerufen, um dies aufzurufen (obwohl in Scala versteckt; in Java mit int-> offensichtlicher Integer). Die Standardimplementierung vergleicht lediglich Referenzen (wie in Java).

Die Any.==(Any)Methode vergleicht zwei Objekte und lässt zu, dass jedes Argument null ist (als würde eine statische Methode mit zwei Instanzen aufgerufen). Es vergleicht, wenn beide sind null, und ruft die equals(Any)Methode für eine Box-Instanz auf.

Die AnyRef.eq(AnyRef)Methode vergleicht nur Referenzen, dh dort befindet sich die Instanz im Speicher. Für diese Methode gibt es kein implizites Boxen.

Beispiele

  • 1 equals 2wird zurückkehren false, wenn es umleitetInteger.equals(...)
  • 1 == 2wird zurückkehren false, wenn es umleitetInteger.equals(...)
  • 1 eq 2 wird nicht kompiliert, da beide Argumente vom Typ sein müssen AnyRef
  • new ArrayList() equals new ArrayList()wird zurückkehren true, wenn es den Inhalt überprüft
  • new ArrayList() == new ArrayList()wird zurückkehren true, wenn es umleitetequals(...)
  • new ArrayList() eq new ArrayList()wird zurückgegeben false, da beide Argumente unterschiedliche Instanzen sind
  • foo equals foowird zurückkehren true, es foosei denn, ist null, dann wird ein werfenNullPointerException
  • foo == foowird zurückkehren true, auch wenn fooistnull
  • foo eq foowird zurückgegeben true, da beide Argumente auf dieselbe Referenz verweisen
zjohn4
quelle
6

Es gibt einen interessanten Unterschied zwischen ==und equalsfür Floatund DoubleTypen: Sie behandeln NaNunterschiedlich:

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

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

Bearbeiten: Wie in einem Kommentar erwähnt - "das passiert auch in Java" - hängt davon ab, was genau das ist:

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

Dies wird gedruckt

false
true
true

Die unboxedNanAusbeuten werden also falseim Vergleich zur Gleichheit verglichen, da IEEE-Gleitkommazahlen dies so definieren und dies wirklich in jeder Programmiersprache geschehen sollte (obwohl dies irgendwie mit dem Begriff der Identität zu tun hat).

Das Boxed NaN ergibt true für den Vergleich mit ==Java, wenn wir Objektreferenzen vergleichen.

Ich habe keine Erklärung für den equalsFall, IMHO sollte es sich wirklich genauso verhalten wie ==bei Doppelwerten ohne Box, aber das tut es nicht.

In Scala übersetzt ist die Angelegenheit etwas komplizierter, da Scala die primitiven und Objekttypen vereinheitlicht und je Anynach Bedarf in das primitive Double und das Boxed Double übersetzt hat. Daher läuft die Scala ==anscheinend auf einen Vergleich primitiver NaNWerte hinaus, equalsverwendet jedoch diejenige, die für Boxed-Double-Werte definiert ist (es gibt eine Menge impliziter Konvertierungsmagie und es gibt Dinge, die auf Double-Werte gepimpt werden RichDouble).

Wenn Sie wirklich herausfinden müssen, ob tatsächlich etwas NaNverwendet wird isNaN:

Scravy
quelle
und das passiert auch in Java!
Iwan Satria
4

Suchen Sie in Scala == zuerst nach Nullwerten und rufen Sie dann die Methode equals für das erste Objekt auf

Jack
quelle