在Scala中,“==”和“.equals”的区别是什么?

167

==.equals() 在 Scala 中的区别是什么?何时使用它们?

这两者在实现上是否与 Java 相同?

编辑:相关问题中讨论了特定情况下 AnyVal 的比较。更一般的情况是比较 Any。


可能是[为什么在Scala中,对于AnyVal的值,==运算符和equals()方法的行为不同]的重复问题(https://dev59.com/VWMl5IYBdhLWcg3wV10F)。 - Ben
@Ben 我认为那个问题应该被标记为重复,考虑到提问的日期。此外,我觉得这两个问题是不同的。 - Jus12
scala.Equals中有一个scaladoc,它指向《Scala编程》第28章,对象相等性 - Kyr
5个回答

223

通常使用 == ,它会路由到equals,但它会正确处理null。引用相等性(很少使用)是eq


12
使用Java库时也适用吗? - Jus12
20
可以。例如,新的 java.util.ArrayListInt == 新的 java.util.ArrayListInt,因为 ArrayList 的 equals 方法是内容相等。 - Didier Dupont
5
对于 Int 和 Long 类型,以及 == 运算符和 .equals() 方法之间存在一些奇怪的行为。将同一个数字作为 Int 和 Long 进行比较,使用 == 运算符得到的结果是 true,而使用 equals 方法得到的结果是 false。因此,== 并不总是调用 equals 方法。 - Harold L
25
有趣的是,3 == BigInt(3)BigInt(3) == 3 都是成立的。但是,3.equals(BigInt(3)) 是错误的,而 BigInt(3).equals(3) 是正确的。因此,在Scala中最好使用==而不是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) 为假? - Eastsun

39

TL;DR

  • 覆盖equals方法以比较每个实例的内容。这是Java中使用的相同equals方法
  • 使用==运算符进行比较,无需担心null引用
  • 使用eq方法检查两个参数是否是完全相同的引用。建议不要使用,除非您了解其工作原理,并且通常equals将能够满足您的需求。请确保仅对AnyRef参数使用此选项,而不仅限于Any

注意:在equals的情况下,就像在Java中一样,如果交换参数可能不会返回相同的结果,例如1.equals(BigInt(1))将返回false,反之将返回true。这是因为每个实现仅检查特定类型。 原始数字不会检查第二个参数是否为NumberBigInt类型,而只检查其他原始类型

Details

AnyRef.equals(Any)方法由子类覆盖。这是从Java规范传递到Scala的方法。如果在未装箱的实例上使用,它将被装箱以调用该方法(尽管在Scala中隐藏;在Java中更为明显,例如int->Integer)。默认实现仅比较引用(与Java相同)

Any.==(Any)方法比较两个对象,并允许任一参数为空(就像使用两个实例调用静态方法一样)。 它将比较它们是否都为null,然后调用装箱实例上的equals(Any)方法。

AnyRef.eq(AnyRef)方法仅比较引用,即实例在内存中的位置。 对于此方法没有隐式的装箱操作。

Examples

  • 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,除非foonull,那么会抛出NullPointerException
  • foo == foo会返回true,即使foonull
  • foo eq foo会返回true,因为两个参数链接到同一个引用

你能解释一下 Scala 中的 === 吗? - user2441441

38

==是一个终态(final)方法,调用了.equals方法,而该方法不是终态。

这与Java截然不同,Java中==是一个运算符而非方法,且针对对象的严格比较引用相等性。


8

==equalsFloatDouble类型中有一个有趣的区别:它们对待NaN的方式不同:

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

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

编辑:正如评论中指出的那样——“这也会发生在Java中”——这取决于this是什么:

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

因为IEEE浮点数是这样定义的,所以与unboxedNan比较时会返回false。尽管它会影响标识的概念,但在每种编程语言中都应该如此。
使用==进行比较时,Java中的boxed NaN返回true,因为我们正在比较对象引用。
我没有equals情况的解释,我认为它应该像未装箱的双精度值上的==一样表现,但它并没有。
转换为Scala后,问题会变得有些复杂,因为Scala已将基本类型和对象类型统一为Any,并根据需要将其转换为基本double和装箱的Double类型。因此,Scala中的==显然归结为原始NaN值的比较,但equals使用装箱Double值上定义的值(存在许多隐式转换魔法,并且由RichDouble强化了某些属性)。
如果您确实需要查看某个东西是否实际上是NaN,请使用isNaN

这也发生在Java中! - Iwan Satria

6
在Scala中,==首先检查Null值,然后调用第一个对象的equals方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接