¿Cuál es la diferencia entre == y .equals en Scala?

144

¿Cuál es la diferencia entre ==y .equals()en Scala, y cuándo usar cuál?

¿La implementación es la misma que en Java?

EDITAR: La pregunta relacionada se refiere a casos específicos de AnyVal. El caso más general es Any.

Jus12
fuente
@Ben Creo que otra pregunta debería marcarse como duplicada teniendo en cuenta la fecha de la consulta. Además, siento que las dos preguntas son diferentes.
Jus12

Respuestas:

201

Normalmente se utiliza ==, se dirige a equals, excepto que trata nulls correctamente. La igualdad de referencia (raramente utilizada) es eq.

Didier Dupont
fuente
12
¿También se aplica cuando se usan bibliotecas Java?
Jus12
20
Lo hace. Por ejemplo new java.util.ArrayList [Int] () == new java.util.ArrayList [Int] (), ya que igual en ArrayList es igualdad de contenido.
Didier Dupont el
55
También hay un comportamiento extraño alrededor de Int y Long y == versus .equals (). El mismo número que Int y Long devuelve verdadero para == pero falso para igual. Entonces == no siempre se enruta a iguales.
Harold L
24
Más interesante, ambos 3 == BigInt(3)y BigInt(3) == 3son verdad. Pero, 3.equals(BigInt(3))es falso, mientras que BigInt(3).equals(3)es cierto. Por lo tanto, prefiera usar ==. Evite usar equals()en scala. Creo ==que la conversión implícita funciona bien, pero equals()no lo hace.
Naetmul
Entonces, ¿por qué new java.lang.Integer(1) == new java.lang.Double(1.0)es cierto mientras que new java.lang.Integer(1) equals new java.lang.Double(1.0)es falso?
Eastsun
34

==es un método final y llama .equals, que no es definitivo.

Esto es radicalmente diferente de Java, donde ==es un operador en lugar de un método y compara estrictamente la igualdad de referencia para los objetos.

Don Roby
fuente
29

TL; DR

  • equalsMétodo de anulación para comparar el contenido de cada instancia. Este es el mismo equalsmétodo utilizado en Java
  • Utilice el ==operador para comparar, sin preocuparse por las nullreferencias.
  • Use el eqmétodo para verificar si ambos argumentos son EXACTAMENTE la misma referencia. Se recomienda no usar a menos que comprenda cómo funciona esto y, a menudo equals, funcionará para lo que necesita. Y asegúrese de usar esto solo con AnyRefargumentos, no soloAny

NOTA: en el caso de equals, al igual que en Java, es posible que no devuelva el mismo resultado si cambia los argumentos, por ejemplo 1.equals(BigInt(1)), devolverá falsedonde regresará el inverso true. Esto se debe a que cada implementación verifica solo tipos específicos. Los números primitivos no comprueban si el segundo argumento es de tipo Numberni de BigInttipo, sino solo de otros tipos primitivos

Detalles

El AnyRef.equals(Any)método es el anulado por las subclases. Un método de la especificación Java que también se ha introducido en Scala. Si se usa en una instancia sin caja, está en caja para llamar a esto (aunque está oculto en Scala; más obvio en Java con int-> Integer). La implementación predeterminada simplemente compara referencias (como en Java)

El Any.==(Any)método compara dos objetos y permite que cualquiera de los argumentos sea nulo (como si se llamara a un método estático con dos instancias). Compara si ambos son null, luego llama al equals(Any)método en la instancia en caja.

El AnyRef.eq(AnyRef)método compara solo referencias, que es donde se encuentra la instancia en la memoria. No hay boxeo implícito para este método.

Ejemplos

  • 1 equals 2regresará false, ya que redirige aInteger.equals(...)
  • 1 == 2regresará false, ya que redirige aInteger.equals(...)
  • 1 eq 2 no se compilará, ya que requiere que ambos argumentos sean de tipo AnyRef
  • new ArrayList() equals new ArrayList()regresará true, ya que verifica el contenido
  • new ArrayList() == new ArrayList()regresará true, ya que redirige aequals(...)
  • new ArrayList() eq new ArrayList()regresará false, ya que ambos argumentos son instancias diferentes
  • foo equals fooregresará true, a menos que foosea null, luego arrojará unNullPointerException
  • foo == foovolverá true, incluso si fooesnull
  • foo eq foovolverá true, ya que ambos argumentos enlazan con la misma referencia
zjohn4
fuente
6

Hay una diferencia interesante entre ==y equalspara Floaty Doubletipos: tratan de manera NaNdiferente:

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

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

Editar: como se señaló en un comentario, "esto también sucede en Java", depende de qué es exactamente esto :

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

Esto imprimirá

false
true
true

Entonces, los unboxedNanrendimientos falseen comparación con la igualdad porque así es como lo definen los números de punto flotante IEEE y esto realmente debería suceder en todos los lenguajes de programación (aunque de alguna manera interfiere con la noción de identidad).

El NaN encuadrado rinde verdadero para la comparación usando ==en Java ya que estamos comparando referencias de objeto.

No tengo una explicación para el equalscaso, en mi humilde opinión, realmente debería comportarse igual que ==en los valores dobles sin caja, pero no lo hace.

Traducido a Scala, el asunto es un poco más complicado, ya que Scala ha unificado tipos primitivos y de objetos en Anyy se traduce en el doble primitivo y el Doble en caja según sea necesario. Por lo tanto, la escala ==aparentemente se reduce a una comparación de NaNvalores primitivos , pero equalsusa el definido en valores dobles en caja (hay mucha magia de conversión implícita y hay cosas que se combinan en dobles RichDouble).

Si realmente necesita saber si algo se NaNusa realmente isNaN:

scravy
fuente
¡Y esto también sucede en Java!
Iwan Satria
4

En Scala == primero verifique los valores nulos y luego llame al método igual en el primer objeto

Jack
fuente