Scala如何将Int转换为Double?

7
val d: Double = 42

当我尝试在Intellij中查找隐式转换时,没有什么有趣的结果。此外,Int不是Double的子类型。那么Scala是如何实现它的呢?


可能是如何在Scala中进行类型转换?的重复问题。 - Stoopkid
@Stoopkid 从某种意义上说,我的问题与那个线程相反:我几乎是在问为什么我不需要一个.toDouble - allidoiswin
1个回答

12
长话短说:这不是一些伴生对象上的普通隐式转换,数值类型得到了特殊处理。
如果我们在这个脚本上运行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$1 extends Object {
    private[this] val d: Double = _;
    <stable> <accessor> private def d(): Double = anon$1.this.d;
    def <init>(): <$anon: Object> = {
      anon$1.super.<init>();
      anon$1.this.d = 42.0;
      ()
    }
  }
}

在去糖代码中,我们看到一个双精度字面量42.0,但没有调用任何转换函数(例如来自Predef的函数)。因此,从IntDouble的转换必须发生在编译的早期阶段,而不是在运行时。
规范的第3.5.3节告诉我们,由于弱一致性关系<:w的可传递性,Int 弱一致Double
Int <:w Long <:w Float <:w Double

此外,6.26.1节(值转换)告诉我们,如果一个类型为T的表达式e出现在期望类型为pt的表达式位置上,并且T弱符合pt,则可以应用“数值扩展”的规则。在这种情况下,我们可以使用以下规则:
  • 表达式e = 42
  • 表达式的类型T = Int
  • 期望类型pt = Double
因此,42将使用toDouble转换为42.0。由于它是一个可以在编译时处理的常量,所以我们在去糖后的代码中看不到toDouble。然而,如果我们使用一个非常量值去糖类似的程序,则会看到toDouble
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$1 extends Object {
    private[this] val d: Double = _;
    <stable> <accessor> private def d(): Double = anon$1.this.d;
    def <init>(): <$anon: Object> = {
      anon$1.super.<init>();
      anon$1.this.d = new scala.util.Random().nextInt(42).toDouble();
      ()
    }
  }
}

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


非常有用的信息,特别是关于弱一致性细节方面。那么,我之前对于底层进行的整型到双精度浮点型隐式转换的假设是不正确的。 - Leo C
@LeoC 这是一个合理的假设。虽然我无法自发地想出一个例子,在不查看解糖代码的情况下,人们实际上会注意到差异。 - Andrey Tyukin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接