【问题标题】:Implicit conversion from Int to Double in scala doesn't workscala中从Int到Double的隐式转换不起作用
【发布时间】:2016-08-16 04:20:16
【问题描述】:

我写了一些如下所示的隐式代码,我想知道为什么不调用 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
  }
}

规格

  • scalac 版本是 2.11.8。

【问题讨论】:

标签: scala implicit-conversion scalac


【解决方案1】:

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

起初,我认为这是因为 Scala 是如何解决隐含的。不知何故,我认为this implicit in Int.scala 被优先考虑了。优先级规则通常很直观,但对于像这样的边缘情况,我们参考6.26.3 Overloading Resolution。不过让我感到困惑的是,对int2double 的调用不存在——它已经内联到.toDouble!!

深入挖掘后,似乎存在一个关于值类型的极端情况,适用于从IntDouble 的转换。名为weak conformance 的东西决定了我们如何转换Byte -&gt; Short -&gt; Int -&gt; Long -&gt; Float -&gt; Double。所以,总而言之,我认为你不能推翻这个内置转换......

这种转换称为numeric widening

数值加宽

如果e 具有弱符合预期类型的​​原始数字类型,则将其扩展为预期类型 使用一种数字转换方法toShorttoChartoInt, toLong, toFloat, toDouble...

编辑

另外,如果有人想知道为什么这是一件事,来自Martin Odersky(这是一个旧链接 - 一般不要相信这里所说的),我们会遇到如果我们没有这些额外的特殊转换,常见问题:

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

不是很直观...

【讨论】:

    【解决方案2】:

    查看 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 的answer 关于扩大: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

    弱一致性 在某些情况下,Scala 使用更一般的一致性关系。类型 >SS 弱符合类型 TT,写作 S<:wts s>SS 和 TT 都是原始数字类型,并且 SS 在以下 >ordering 中位于 TT 之前。

    字节 <:w>

    短 <:w>

    字符 <:w>

    整数 <:w>

    长 <:w>

    浮点 <:w>

    弱最小上界是关于弱一致性的最小上界。

    【讨论】:

    • 使用-Ywarn-numeric-widen 使转化可见。
    • 误导性地,在这种情况下,隐含与问题无关。
    • @Yaneeve Alecs 回答很好。
    • @Alec 您引用的 ML 线程包括在 2.10 中用作后备机制以及在 2.11 中用于视图的伴随隐式方法的示例。
    • @Yaneeve 请原谅,另一个答案是邮件列表线程的“旧链接”,这仍然是经典的。例如,参见 Paul Phillips 消息,它在 2.10 中使用了转换方法,但在 2.11 中没有; Jason Zaugg 表示,如果你想要 Int =&gt; Double 等,这些方法在 2.11 中仍然可用。
    猜你喜欢
    • 2015-02-05
    • 2018-11-19
    • 2011-05-01
    • 2010-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多