Scala 如何将 Int 转换为 Double?

How does Scala convert Int to Double?

val d: Double = 42

当我尝试通过 intellij 查找隐式转换时,没有发现任何有趣的东西。此外,Int 不是 Double 的子类型。那么 Scala 是如何做到的呢?

长话短说:这不是对某些伴随对象的普通隐式转换,数字类型得到特殊处理。


如果我们 运行 scala -print 在此脚本上:

val d: Double = 42

我们得到:

package <empty> {
  object Main extends Object {
    def main(args: Array[String]): Unit = {
      new <$anon: Object>();
      ()
    };
    def <init>(): Main.type = {
      Main.super.<init>();
      ()
    }
  };
  final class anon extends Object {
    private[this] val d: Double = _;
    <stable> <accessor> private def d(): Double = anon.this.d;
    def <init>(): <$anon: Object> = {
      anon.super.<init>();
      anon.this.d = 42.0;
      ()
    }
  }
}

在脱糖后的代码中,我们看到了双重文字 42.0,但没有调用任何转换 函数(例如来自 Predef)。因此,从 IntDouble 的转换必须不发生 在 运行 时间,但在编译的早期阶段。

section 3.5.3 of the specification 告诉我们 Int 弱符合 Double 因为弱符合关系 <:w:

的传递性
Int <:w Long <:w Float <:w Double

此外,Section 6.26.1 (Value Conversions) 告诉我们 numeric widening 的规则适用于类型 T 的表达式 e 出现在 类型 pt 是预期的,T 弱符合 pt。在这种情况下,我们可以应用规则

  • 表达式e = 42
  • 表达式类型 T = Int
  • 预期类型pt = Double

因此,42 使用 toDouble 转换为 42.0。因为它是一个可以在编译时处理的常量, 我们在脱糖代码中看不到 toDouble。但是,如果我们用 non-constant 对类似的程序进行脱糖 值

val d: Double = (new scala.util.Random).nextInt(42)

我们得到:

package <empty> {
  object Main extends Object {
    def main(args: Array[String]): Unit = {
      new <$anon: Object>();
      ()
    };
    def <init>(): Main.type = {
      Main.super.<init>();
      ()
    }
  };
  final class anon extends Object {
    private[this] val d: Double = _;
    <stable> <accessor> private def d(): Double = anon.this.d;
    def <init>(): <$anon: Object> = {
      anon.super.<init>();
      anon.this.d = new scala.util.Random().nextInt(42).toDouble();
      ()
    }
  }
}

并且 toDouble 在那里,如指定的那样。