为什么== operator和equals()对Scala中的AnyVal值的行为有所不同

时间:2013-08-28 04:12:39

标签: scala equals equality primitive

scala.Any的标量中,解释了运算符==(或方法==):

  

表达式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如果我们不将1 eq null转换为java.lang.Integer,则不会编译。此外,1==表现不同。

我会举一些例子。

scala> 1 == 1
res0: Boolean = true

scala> 1 == 1.0
res1: Boolean = true

scala> 1 == 1.2
res2: Boolean = false

scala> 2 == BigInt(2)
res3: Boolean = true

scala> 2.0 == BigInt(2)
res4: Boolean = true

scala> 2 == BigInt(3)
res5: Boolean = false

到目前为止,没有什么是奇怪的。但是,如果我们使用equals()方法执行相同的操作,

scala> 1 equals 1
res7: Boolean = true

scala> 1 equals 1.0
res8: Boolean = false

scala> 1 equals 1.2
res9: Boolean = false

scala> 2 equals BigInt(2)
res10: Boolean = false

scala> 2.0 equals BigInt(2)
res11: Boolean = false

scala> 2 equals BigInt(3)
res12: Boolean = false

因此,如果类型不同,则equals()始终返回false,而==如果它们转换为相同类型则表示它们表示相同的值。

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

scala> BigInt(2) == 2
res25: Boolean = true

scala> BigInt(2) == 2.0
res26: Boolean = true

scala> BigInt(3) == 2
res27: Boolean = false

scala> BigInt(2) equals 2
res28: Boolean = true

scala> BigInt(2) equals 2.0
res29: Boolean = true

scala> BigInt(3) equals 2
res30: Boolean = false

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

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

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

编辑2
虽然有答案,但我的问题仍然存在。我会打开这个问题
与Java中的AnyValscala.Int对应的是Java的原始类型scala.Longint
在Java中,longjava.lang.Integer是类,因此它们的变量是引用,可以有java.lang.Long。 这意味着,它们就像Scala中的null一样。不是AnyRef
Scala的AnyVal - AnyValscala.Int不能包含scala.Long值,Java nullint也不能。{ 此外,Java中的long java.lang.Integer用于引用相等(与Scala中的==相同)。
在Scala REPL中使用eq所获得的内容与使用.java源文件的纯Java项目中的内容完全不同。

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

java.lang.Integer

输出:

true
false
false
true
false
是的,它们的行为类似于scala AnyVal的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的equals()的{​​{1}}是否与Java的原始类型的AnyVal相对应 并且Scala的AnyVal的==是否对应于Java类类的==? 使用BigInt进行相等测试怎么样? Java中没有相应的原始类型 问题仍然存在......

编辑3
我可以从scaladoc找到一些信息。 (http://www.scala-lang.org/api/current/index.html#scala.Int
Shadowed Implicit Value Members 项目中的隐含信息
我发现equals()equals()==以及...,
已超载Char Short会调用隐式转化Float==int2double
int2float仅为int2long定义,而它会调用隐式转化equals()
也就是说,Any将与int2Integer相同
还有一个问题:
为什么Int.equals()的{​​{1}}被重载,而java.lang.Integer.equals()的{​​{1}}没有超载?

2 个答案:

答案 0 :(得分:11)

相关讨论是描述性的

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
}

答案 1 :(得分: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