当范围内存在多个不明确的隐式时,Scala 编译器如何选择隐式

How implicit is chosen by Scala compile when we have multiple ambiguous implicit present in scope

我是 Scala 的新手,我正在参考书 “Scala for Impatient - 第二版”

我正在编写一个小代码,我在其中创建了一个 class Fraction,它作为两个 Int 字段 num(数字)和 den(分母)。 class 有一个方法 * 执行 numden 和 returns 新 Fraction 实例的乘法。

在这里,为了理解 implicit 的工作原理,我创建了一个对象 FractionConversions,它有助于两个隐式方法 intToFractionfractionToDouble.

在测试 ImplicitConversionTester 中的代码时,我导入了 FractionConversions._ 路径,因此编译器可以使用这两个隐式方法。

现在,参考代码,让图片更清晰。

package OperatorOverloadingAndImplicitConversion
    
    class Fraction(n : Int, d : Int) {
      private val num = this.n
      private val den = this.d
    
      def *(other : Fraction) = new Fraction(num*other.num, den*other.den)
    
      override def toString: String = {
        s"Value of Fraction is : ${(num*0.1)/den}"
      }
    }
    
    object Fraction {
      def apply(n: Int, d: Int): Fraction = new Fraction(n, d)
    }
    
    object FractionConversions {
    
      implicit def fractionToDouble(f : Fraction): Double = {
        println("##### FractionConversions.fractionToDouble called ...")
       (f.num*0.1)/f.den
      }
    
      implicit def intToFraction(n : Int) : Fraction = {
        println("##### FractionConversions.intToFraction called ...")
        new Fraction(n,1)
      }
    }
    
    
    object ImplicitConversionTester extends App {
    
      import FractionConversions._
    
      /*
       * CASE 1 : Here,  "fractionToDouble" implicit is called. 
Why "intToFraction" was eligible but not called ? 
    
       */
      val a = 2 * Fraction(1,2)
    
      /*
       * CASE 2 : Works as expected. 
Here, number "2" is converted to Fraction using "intToFraction" implicit.
       */
      val b = 2.den
    
      /*
       * CASE 3: Why again "intToFraction" but not "fractionToDouble" ? Why ?
       */
      val c = Fraction(4,5) * 3
      println(a) // output : 0.1 FractionConversions.fractionToDouble called
      println(b) // output : 1 FractionConversions.intToFraction called
      println(c) // output : Value of Fraction is : 0.24000000000000005. FractionConversions.intToFraction called
    }

我在上面的代码中有查询:

这里,我试图复制书中提到的以下场景,所以出现了上述问题。

那么,我们是否应该总结一下,如果左侧操作数和 select 右侧操作数都符合转换条件,则编译器会避免进行转换?例如,在 (a*b) 的情况下,即使 ab 都符合转换条件,a 总是被忽略并且 b 被转换为预期类型 ?

您的描述是正确的:对于表达式 a.f(b)(注意 a*b 只是 a.*(b) 的运算符符号),适用于 b 的隐式转换允许要进行类型检查的表达式将优先于适用于 a 的隐式转换。出现这种情况是因为 a 的隐式转换(在语言标准中称为“视图”)只有在 *(在 Int 中)不适用于 Fraction 时才能尝试(参见 SLS 7.3)...但是 SLS 6.6 中的适用性定义表明隐式视图足以适用。

因此,如果想将 2 隐式转换为 Fraction 以使用 Fraction.*,则需要

(2: Fraction) * Fraction(1, 2)

同样,对于情况 3,如果您希望首先将分数隐式转换为 Double

(Fraction(4,5): Double) * 3