(Number,Number)匹配(Float,Int)但不匹配(Int,Float)

时间:2010-10-12 14:45:31

标签: scala

它是Scala 2.8.0中的错误吗? (2.8.1.RC2也是如此)

import junit.framework._
import Assert._

class BugTest extends TestCase {

  def compare(first: Any, second: Any): Int = {
      (first, second) match {
        case (k: Int, o: Int) => k compare o
        //why the next case matches (Float, Int) but does not match (Int, Float) ???
        case (k: Number, o: Number) => k.doubleValue() compare o.doubleValue()
        case _ => throw new Exception("Unsupported compare " + first + "; " + second)
    }
  }

  def testCompare() {
    assertEquals("Both Int", -1, compare(0, 1))
    assertEquals("Both Float", 1, compare(1.0, 0.0))
    assertEquals("Float then Int", 0, compare(10.0, 10))
    assertEquals("Int then Float", 0, compare(10, 10.0))//this fails with an exception
  }
}

2 个答案:

答案 0 :(得分:3)

我认为这是一个错误。如果你看一下生成的字节码:


public int compare(java.lang.Object, java.lang.Object);
  Code:
   Stack=4, Locals=7, Args_size=3
   0:   aload_1
   1:   astore_3
   2:   aload_2
   3:   astore  4
   5:   aload_3
   6:   instanceof  #8; //class java/lang/Integer
   9:   ifeq    81
   12:  aload_3
   13:  invokestatic    #14; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   16:  istore  5
   18:  aload   4
   20:  instanceof  #8; //class java/lang/Integer
   23:  ifeq    45
   26:  getstatic   #20; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   29:  iload   5
   31:  invokevirtual   #24; //Method scala/Predef$.intWrapper:(I)Lscala/runtime/RichInt;
   34:  aload   4
   36:  invokestatic    #14; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   39:  invokevirtual   #29; //Method scala/runtime/RichInt.compare:(I)I
   42:  goto    124
   45:  new #31; //class java/lang/Exception

   //part omitted for brevity..

   81:  aload_3
   82:  instanceof  #54; //class java/lang/Number
   85:  ifeq    161
   88:  aload_3
   89:  checkcast   #54; //class java/lang/Number
   92:  astore  6
   94:  aload   4
   96:  instanceof  #54; //class java/lang/Number
   99:  ifeq    125
   102: getstatic   #20; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   105: aload   6
   107: invokevirtual   #58; //Method java/lang/Number.doubleValue:()D
   110: invokevirtual   #62; //Method scala/Predef$.doubleWrapper:(D)Lscala/runtime/RichDouble;
   113: aload   4
   115: checkcast   #54; //class java/lang/Number
   118: invokevirtual   #58; //Method java/lang/Number.doubleValue:()D

在第6行中,检查第一个变量是java.lang.Integer的实例。如果这不成功,我们继续第81行,该行以java.lang.Number检查开始。如果第一个变量是整数,那么我们继续对第二个变量进行相同的检查。但是,如果第二次检查没有成功,而不是再次在第81行继续进行数字检查,则跳转到第45行,这将引发异常。这似乎不正确。我很快浏览了trac,但无法直接找到关于此的问题,因此创建一个可能是明智的。

修改 正如Extempore指出的那样,这是一个已经存在于trac中的错误,请参阅下面的评论。

答案 1 :(得分:2)

我不知道具体问题的答案,但这是定义比较的另一种方式:

def compare[A : Numeric, B: Numeric](first: A, second: B): Int =
  implicitly[Numeric[A]].toDouble(first) compare implicitly[Numeric[B]].toDouble(second)

scala> compare(0.0,1.0)
res30: Int = -1

scala> compare(0.0,1)
res31: Int = -1

scala> compare(0,1.0)
res32: Int = -1

scala> compare(0,1)
res33: Int = -1