不调用伴随对象中声明的隐式转换

Implicit conversion declared in companion object is not called

我正在 Scala implicits 上尝试示例程序。我正在尝试实施“Scala for the Impatient”一书中的一个解决方案。

我已经写成 Fraction class 有两个成员变量 numeratordenominator 带有操作重载方法 "*(fraction)",这样我们就可以在 Fraction 对象上调用乘法,例如 fration1 * fraction2 * fraction3.

现在,我尝试实现 intToFractionfractionToInt 隐式方法,这样我们也可以编写类似 2[=23= 的语法]分数13。我尝试在 Fraction class 的伴随对象中实现这些方法。但是这些方法没有被调用。

正如 Scala 文档所建议的那样,Scala 编译器将尝试找出伴随对象中的隐式实现。在这里,我们正在对 Fraction class 的对象执行操作,所以我假设,在 Fraction 中声明的隐式应该被自动调用。我也尝试过显式导入隐式方法,但没有用。

请在下面找到代码片段以供参考。

package com.example.implicits.conversion

object ImplicitConversionTester extends  App {
  val f1 = Fraction(4,2)
  val result = 2 * f1
  println(s"${f1 * f1}")
  println(s"${result}")
 
}

class Fraction(val numerator : Int,val denominator : Int) {

  def *(fraction : Fraction): Fraction = {
    println("##### Call to operator overloading *")
    Fraction(this.numerator*fraction.numerator, this.denominator*fraction.denominator)
  }

  override def toString: String = s"Fraction($numerator,$denominator) ===== ${numerator/denominator}"

}

object Fraction {
  def apply(numerator: Int, denominator: Int): Fraction = new Fraction(numerator, denominator)

  implicit def intToFraction(num : Int) = {
    println("##### intToFraction")
    Fraction(num,1)
  }

  implicit def fractionToInt(fr : Fraction) = {
    println("##### fractionToInt")
    fr.numerator/fr.denominator
  }

}

感谢任何解决此问题的建议!

你是对的,scala 编译器首先在

中查找隐式
  • 当时的当前范围
  • 然后在伴随范围内
  • 在导入范围内

在你的例子中,编译器在主单例对象的范围内寻找隐式 ImplicitConversionTester 并且它不会找到任何东西,因为没有任何隐式可用。如果你导入隐式那么它应该工作 -

object Implicit extends App {

import Fraction._

  class Fraction(val numerator : Int,val denominator : Int) {
    def * (fraction : Fraction): Fraction = {
      println("##### Call to operator overloading *")
      Fraction(this.numerator*fraction.numerator, this.denominator*fraction.denominator)
    }
  }
  object Fraction {
    def apply(numerator: Int, denominator: Int): Fraction = new Fraction(numerator, denominator)

    implicit def intToFraction(num : Int) = {
      println("##### intToFraction")
      Fraction(num,1)
    }

    implicit def fractionToInt(fr : Fraction) = {
      println("##### fractionToInt")
      fr.numerator/fr.denominator
    }
  }
  println(Fraction(1,2) * 2)
}

问题出在没有明确提及 return 类型的隐式方法声明中。我没有在上面的代码片段中提到 return 类型。添加函数 return 类型后,隐式函数工作正常。因此,无需在 ImplicitConversionTester class 中显式导入隐式函数来解决问题。

以下是修正错误的更新分数 class:

class Fraction(val numerator : Int,val denominator : Int) {

  val test = 2

  def *(fraction : Fraction): Fraction = {
    println("##### Call to operator overloading *")
    Fraction(this.numerator*fraction.numerator, this.denominator*fraction.denominator)
  }

  override def toString: String = s"Fraction($numerator,$denominator) ===== ${numerator/denominator}"
}

object Fraction {
  def apply(numerator: Int, denominator: Int): Fraction = new Fraction(numerator, denominator)

  implicit def intToFraction(num : Int) : Fraction = { // RETURN TYPE FRACTION
    println("##### intToFraction")
    Fraction(num,1)
  }
  implicit def fractionToInt(fr : Fraction) : Int  = { // ADDED RETURN TYPE INT
    println("##### fractionToInt")
    fr.numerator/fr.denominator
  }

}

在下面分享有关 Scala 编译器如何查找隐式转换的更多详细信息。下图摘自《Scala for the Impatient》一书。

如书中所述,Scala 编译器优先考虑 sourcetarget 类型的伴随对象。

这里,什么是sourcetarget类型,取决于表达式的顺序。例如,如果表达式是“2*Fraction(2,4)”,则表达式从左到右求值。整数“2”已经实现了乘法方法 (*)。因此,编译器首先尝试在“Int”类型中找到从“Fraction to Int”的隐式转换。由于“Int”类型没有“Fraction to Int”的任何实现,编译器将查找 Fraction class 的伴生对象,并在其中找到方法“fractionToInt”。在此示例中,目标类型为 Int,源类型为 Fraction。

考虑表达式 "Fraction(2,4)*2" 的示例,这里作为 Fraction class 具有可用于乘法 "*" 的实现,编译器试图找到将 Int 转换为 Fraction 的隐式。所以目标类型是 Fraction,Source 类型是 Int。由于 Int class 中不存在此类隐式 method/class,编译器查找 Fraction 伴随对象并找到“IntToFraction”隐式。