在 scala 中从 Int 到 Double 的隐式转换不起作用

Implicit conversion from Int to Double in scala doesn't work

我写了一些如下所示的隐式代码,我想知道为什么 i2d 函数隐式对话没有被调用。

object Test {
  implicit def i2d(x: Int): Double = {
    println("foo")
    x.toDouble
  }

  implicit def d2i(x: Double): Int = {
    x.toInt
  }

  val x: Int = 10
  val y: Double = x
  val z: Int = 3.5
}

scalac -Xprint:typer Test.scala

输出
// Test.scala
[[syntax trees at end of typer]] 
package <empty> {
  object Test extends scala.AnyRef {
    def <init>(): Test.type = {
      Test.super.<init>();
      ()
    };
    implicit def i2d(x: Int): Double = {
      scala.this.Predef.println("foo");
      x.toDouble
    };
    implicit def d2i(x: Double): Int = x.toInt;
    private[this] val x: Int = 10;
    <stable> <accessor> def x: Int = Test.this.x;
    private[this] val y: Double = Test.this.x.toDouble;
    <stable> <accessor> def y: Double = Test.this.y;
    private[this] val z: Int = Test.this.d2i(3.5);
    <stable> <accessor> def z: Int = Test.this.z
  }
}

规格

查看 Int 伴随对象中的最后一行。我认为这与观点的概念有关:

object Int extends AnyValCompanion {
  /** The smallest value representable as a Int.
   */
  final val MinValue = java.lang.Integer.MIN_VALUE

  /** The largest value representable as a Int.
   */
  final val MaxValue = java.lang.Integer.MAX_VALUE

  /** Transform a value type into a boxed reference type.
   *
   *  @param  x   the Int to be boxed
   *  @return     a java.lang.Integer offering `x` as its underlying value.
   */
  def box(x: Int): java.lang.Integer = java.lang.Integer.valueOf(x)

  /** Transform a boxed type into a value type.  Note that this
   *  method is not typesafe: it accepts any Object, but will throw
   *  an exception if the argument is not a java.lang.Integer.
   *
   *  @param  x   the java.lang.Integer to be unboxed.
   *  @throws     ClassCastException  if the argument is not a java.lang.Integer
   *  @return     the Int resulting from calling intValue() on `x`
   */
  def unbox(x: java.lang.Object): Int = x.asInstanceOf[java.lang.Integer].intValue()

  /** The String representation of the scala.Int companion object.
   */
  override def toString = "object scala.Int"

  /** Language mandated coercions from Int to "wider" types.
   */
  implicit def int2long(x: Int): Long = x.toLong
  implicit def int2float(x: Int): Float = x.toFloat
  implicit def int2double(x: Int): Double = x.toDouble
}

另请参阅:Where does Scala look for implicits?

编辑

根据@som-snytt 的评论:

scala -Ywarn-numeric-widen
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_91).
Type in expressions for evaluation. Or try :help.

scala> object Test {
     |   implicit def i2d(x: Int): Double = {
     |     println("foo")
     |     x.toDouble
     |   }
     |
     |   implicit def d2i(x: Double): Int = {
     |     x.toInt
     |   }
     |
     |   val x: Int = 10
     |   val y: Double = x
     |   val z: Int = 3.5
     | }
<console>:22: warning: implicit numeric widening
         val y: Double = x
                         ^

此外,添加到@Alec 的 about widening: http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#value-conversions

来自:http://www.scala-lang.org/files/archive/spec/2.11/03-types.html#weak-conformance

Weak Conformance In some situations Scala uses a more general conformance relation. A type >SS weakly conforms to a type TT, written S<:wTS<:wT, if S<:TS<:T or both >SS and TT are primitive number types and SS precedes TT in the following >ordering.

Byte <:w<:w Short

Short <:w<:w Int

Char <:w<:w Int

Int <:w<:w Long

Long <:w<:w Float

Float <:w<:w Double

A weak least upper bound is a least upper bound with respect to weak conformance.

这比我想象的要复杂得多。

起初,我认为这是因为 Scala 解析隐式的方式。不知何故,我想到了this implicit in Int.scala was getting prioritized. The rules for priority are usually intuitive, but for edge cases like this, we refer to 6.26.3 Overloading Resolution。令我困惑的是 int2double 的调用不存在 - 它已经内联到 .toDouble!!

进一步研究后,似乎有一种关于值类型的边缘情况适用于从 IntDouble 的转换。一个叫做 weak conformance 的东西决定了我们如何转换 Byte -> Short -> Int -> Long -> Float -> Double。所以,总而言之,我认为你不能否决这个内置转换...

此转换称为 numeric widening

Numeric Widening

If e has a primitive number type which weakly conforms to the expected type, it is widened to the expected type using one of the numeric conversion methods toShort, toChar, toInt, toLong, toFloat, toDouble...

编辑

此外,如果有人想知道 为什么 这是一个东西,来自 Martin Odersky(这是一个旧的 link - 不要相信什么一般在这里说),如果我们没有这些额外的特殊转换,我们 运行 会遇到常见问题:

scala-hypothetical> val x = List(1, 2.0) 
x: List[AnyVal]

Not very intuitive...