【问题标题】:Why == operator and equals() behave differently for values of AnyVal in Scala为什么 == 运算符和 equals() 对于 Scala 中 AnyVal 的值的行为不同
【发布时间】:2013-08-31 00:30:06
【问题描述】:

scala.Any的scaladoc中,解释了操作符==(或者,方法==):

表达式x == that 等价于if (x eq null) that eq null else x.equals(that) http://www.scala-lang.org/api/current/#scala.Any

对于AnyRef的子类的对象,我很容易理解,也没有看到什么奇怪的东西。

但是,对于 AnyVal 的值,(我的意思是 IntDoubleLong 等等),上面的定义有点棘手(1 eq null?如果我们这样做,这不会编译不将 1 转换为 java.lang.Integer)。此外,==equals() 的行为也不同。

我会举一些例子。

斯卡拉> 1 == 1 res0: 布尔 = 真 斯卡拉> 1 == 1.0 res1:布尔=真 斯卡拉> 1 == 1.2 res2: 布尔 = 假 scala> 2 == BigInt(2) res3: 布尔 = 真 scala> 2.0 == BigInt(2) res4:布尔=真 scala> 2 == BigInt(3) res5: 布尔 = 假

到目前为止,没有什么是奇怪的。但是如果我们用equals() 方法做同样的事情,

scala> 1 等于 1 res7: 布尔 = 真 scala> 1 等于 1.0 res8: 布尔 = 假 scala> 1 等于 1.2 res9: 布尔 = 假 scala> 2 等于 BigInt(2) res10: 布尔 = 假 scala> 2.0 等于 BigInt(2) res11: 布尔 = 假 scala> 2 等于 BigInt(3) res12: 布尔 = 假

所以如果类型不同,equals() 总是返回 false,而 == 测试它们是否表示相同的值,如果它们被转换为相同的类型。

对于AnyRef 的子类,方法==equals() 返回相同。

scala> BigInt(2) == 2 res25: 布尔 = 真 scala> BigInt(2) == 2.0 res26:布尔=真 scala> BigInt(3) == 2 res27: 布尔 = 假 scala> BigInt(2) 等于 2 res28: 布尔 = 真 scala> BigInt(2) 等于 2.0 res29: 布尔 = 真 scala> BigInt(3) 等于 2 res30: 布尔 = 假

那么,为什么 ==equals() 方法与 AnyVal 不同?

我使用的是 Scala 版本 2.10.2(Java HotSpot(TM) 64 位服务器 VM,Java 1.7.0_25)。

编辑 1
我看到 == 不能被直接覆盖,因为它根据Programming in Scala, 2nd Edition 定义为 Any 类中的最终方法。

编辑 2
虽然有答案,但我的问题仍然存在。我将保留这个问题。

Java中的scala.Intscala.Long对应的是Java的基本类型intlong
在Java中,java.lang.Integerjava.lang.Long是类,所以它们的变量是引用,可以有null。 这意味着,它们就像 Scala 中的AnyRef。不是AnyVal
Scala 的 AnyVal - scala.Intscala.Long 不能有 null 值,Java 的 intlong 也不能。
此外,Java 中的 java.lang.Integer== 用于引用相等(与 Scala 中的 eq 相同)。
在这方面,您在 Scala REPL 中使用 java.lang.Integer 所获得的结果将与在带有 .java 源文件的纯 Java 项目中获得的结果完全不同。

但是,在 Java 中使用原始类型类可以得到:(这是 JAVA)

class Main {
    public static void main(String[] args) {
        System.out.println(String.valueOf(new java.lang.Integer(1).equals(1)));
        System.out.println(String.valueOf(new java.lang.Integer(1).equals(1L)));
        System.out.println(String.valueOf(new java.lang.Integer(1).equals(1.0)));
        System.out.println(String.valueOf(new java.lang.Integer(1).equals(new java.lang.Integer(1))));
        System.out.println(String.valueOf(new java.lang.Integer(1).equals(new java.lang.Long(1))));
    }
}

输出:

真的 错误的 错误的 真的 假的 是的,它们的行为类似于 scala AnyVal 的 equals()。但是,那么,为什么会发生这种情况呢?

Scala 的AnyVal== 是否对应Java 原始类型的==
Scala 的 AnyVal 的 equals() 是否对应于 Java 类类型的 equals()
BigInt 的相等性测试怎么样? Java中没有对应的原始类型。
问题仍然存在......

编辑 3
我可以从 scaladoc 中找到一些信息。 (http://www.scala-lang.org/api/current/index.html#scala.Int)
来自Shadowed Implicit Value Members项的隐含信息
我可以发现==CharShortFloat 和...重载,
== 将调用隐式转换int2doubleint2floatint2long
equals() 只为Any 定义,它会调用隐式转换int2Integer
也就是说,Int.equals() 将与 java.lang.Integer.equals() 相同。

还有一个问题:
为什么AnyVal==会超载,而AnyValequals()不会超载?

【问题讨论】:

  • 我很抱歉,但我在整个帖子之后感到困惑。您能否在最后指定确切的问题是什么?
  • @Jatin ==equals() 方法在 Scala 中的 AnyRef 值中是相同的。我认为AnyVal 值也应该相同。事实上,它们是不同的。但是,我在学习 Scala 时找不到任何相关信息。那么,为什么==equals() 对于AnyVal 不一样呢?有没有这方面的规范?
  • @Naetmul,这个输出怎么样:`println(Double.NaN == Double.NaN) println(Double.NaN equals Double.NaN)',我期待的是真的,但输出是假的真的,不明白,任何帮助将不胜感激!!!

标签: scala equals equality primitive


【解决方案1】:

相关讨论是描述性的

spec for == from 2010

和投机的

Rethinking equality from 2011

FWIW,规范要求数值类型 in 12.2 相等。

或者,in HTML。 (在底部引用。)

在 2010 年的“pidgin spec-ese”中,Paul Phillips 是这样说的:

用 == 比较两个原语(装箱或未装箱)应该总是给出 通过将这些值比较为未装箱得到的结果 原语。当您直接调用 equals 时,您将跳过所有这些 软化逻辑,而是采用 java 的理论,即两个盒装 不同类型的值总是不相等的。

除了 12.5 中对Predef 提供的转换的传递引用之外,规范没有提及盒装原语。您通常不需要知道原语何时以其“盒装”形式存储,除非您出于性能原因需要这样做。

因此,例如,这些值会被默默地拆箱并为您提升:

scala> val ds = List(7.0)
ds: List[Double] = List(7.0)

scala> val is = List(7)
is: List[Int] = List(7)

scala> ds(0) == is(0)
res24: Boolean = true

scala> :javap -
  Size 1181 bytes
  MD5 checksum ca732fd4aabb301f3ffe0e466164ed50
  Compiled from "<console>"
[snip]
     9: getstatic     #26                 // Field .MODULE$:L;
    12: invokevirtual #30                 // Method .ds:()Lscala/collection/immutable/List;
    15: iconst_0      
    16: invokevirtual #36                 // Method scala/collection/immutable/List.apply:(I)Ljava/lang/Object;
    19: invokestatic  #42                 // Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D
    22: getstatic     #47                 // Field .MODULE$:L;
    25: invokevirtual #50                 // Method .is:()Lscala/collection/immutable/List;
    28: iconst_0      
    29: invokevirtual #36                 // Method scala/collection/immutable/List.apply:(I)Ljava/lang/Object;
    32: invokestatic  #54                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
    35: i2d           
    36: dcmpl     

你注意到我有点惊讶

2.0 == BigInt(2)  // So far, nothing is strange.

对我来说,这有点神奇。如 Paul Phillips 所述,它会调用 BoxesRunTime.equals

     9: ldc2_w        #22                 // double 2.0d
    12: invokestatic  #29                 // Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
    15: getstatic     #34                 // Field scala/package$.MODULE$:Lscala/package$;
    18: invokevirtual #38                 // Method scala/package$.BigInt:()Lscala/math/BigInt$;
    21: iconst_2      
    22: invokevirtual #44                 // Method scala/math/BigInt$.apply:(I)Lscala/math/BigInt;
    25: invokestatic  #48                 // Method scala/runtime/BoxesRunTime.equals:(Ljava/lang/Object;Ljava/lang/Object;)Z

这是规范,供参考,以这种形式基本上只是承诺做正确的事:

equals 方法测试参数是否为数值类型。 如果这是真的,它将执行适当的 == 操作 对于那种类型。也就是数值类型的equals方法可以 可以认为是这样定义的:

def equals(other: Any): Boolean = other match {
  case that: Byte   => this == that
  case that: Short  => this == that
  case that: Char   => this == that
  case that: Int    => this == that
  case that: Long   => this == that
  case that: Float  => this == that
  case that: Double => this == that
  case _ => false
}

【讨论】:

    【解决方案2】:

    我希望这样做是因为自动装箱和希望与 Java 的期望保持一致,以及一般的数学(1 = 1.0 = 1(表示为长)等)。例如,在 Scala 数值类型和 Java 数值类型之间运行比较:

    scala> val foo: Long = 3L
    foo: Long = 3
    
    scala> val bar: Int = 3
    bar: Int = 3
    
    scala> foo == bar
    res0: Boolean = true
    
    scala> foo.equals(bar)
    res1: Boolean = false
    

    对比:

    scala> val jfoo = new java.lang.Long(3L)
    jfoo: Long = 3
    
    scala> val jbar = new java.lang.Integer(3)
    jbar: Integer = 3
    
    scala> jfoo == jbar
    res2: Boolean = true
    
    scala> jfoo.equals(jbar)
    res3: Boolean = false
    

    【讨论】:

    • 在真正的 Java 中,new java.lang.Long(3L)new java.lang.Integer(3) 都是引用。另外java.lang.Long不能转换为java.lang.Integer;它会抛出java.lang.ClassCastException。如果在转换为相同类型之前比较这些引用,它甚至不会编译:它会生成编译错误:“incomparable types: java.lang.Integer and java.lang.Long”我认为java.lang.Long会自动转换为@ 987654329@,java.lang.Integer 会自动转换成scala.Int。以上代码不会在纯Java中运行。
    猜你喜欢
    • 2015-11-18
    • 2012-01-10
    • 2014-11-13
    • 2014-07-27
    • 2012-11-26
    • 2010-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多