将 Scala 隐式转换纳入范围的问题

Problem with bringing into scope scala implicit conversions

我正在玩隐式函数,但我遇到了一些我不理解的行为。

我定义了一个简单的 class 及其伴随对象(只是为了对隐式进行快速测试)如下

class SimpleNumber(val numb: Int) {
    def sum(n: Int) = numb + n
}

object SimpleNumber {
    implicit def stringToInt(s: String) = s.toInt
}

如果我通过传递字符串而不是 int 来调用 sum 方法,方法 stringToInt 应该可以工作,这样它就可以转换为 int(这只是为了测试目的,所以我不需要检查错误或异常)。

如果我将上面的代码粘贴到 repl (:paste) 中,我会收到此警告

warning: implicit conversion method stringToInt should be enabled by making the implicit value scala.language.implicitConversions visible. This can be achieved by adding the import clause 'import scala.language.implicitConversions' or by setting the compiler option -language:implicitConversions.

所以我转到 VSCode 并粘贴了相同的代码,看看我是否可以通过金属插件获得更多信息,但该警告甚至没有弹出。然后我创建了一个扩展 App 特性的新对象来测试这样的代码

object TestDriver extends App{
    
    val a = new SimpleNumber(4)
    println(a.sum("5"))
}

但我收到以下错误

type mismatch; found : String("5") required: Int

我尝试按照 repl 的建议导入 implicitConversions,首先是在伴随对象中,然后是在 TestDriver 对象中,但是没有有用。然后我直接在 TestDriver 对象中导入了隐式方法,并且成功了。

import SimpleNumber.stringToInt

object TestDriver extends App{
    val a = new SimpleNumber(4)
    println(a.sum("5"))
}

为什么 import scala.language.implicitConversions 不像我想象的那样工作?我正在使用 scala 2.13.3

Why doesn't the import scala.language.implicitConversions work as I thought it would?

import scala.language.implicitConversions 只是表示隐式转换是在代码中定义的,而不是将特定的转换带到范围内。你可以做到

import SimpleNumber._

stringToInt 引入本地范围。

Where does Scala look for implicits?

Should I use method overload then?

取决于你的目标。隐式转换(通常不推荐)不仅仅用于方法重载。在您的示例中,String 可用于所有需要 Int 的地方(这只是举例,通常是标准类型,如 IntStringFunction不应该用于隐含,使用自定义类型隐含)。如果您仅将隐式转换用于方法重载,那么是的,您应该更喜欢后者。不要忘记方法重载不仅可以作为

class SimpleNumber(val numb: Int) {
  def sum(n: Int):    Int = numb + n
  def sum(n: String): Int = numb + n.toInt
}

还有一个type class

class SimpleNumber(val numb: Int) {
  def sum[A: ToInt](n: A): Int = numb + n.toInt
}

trait ToInt[A] {
  def toInt(a: A): Int
}
object ToInt {
  implicit val int: ToInt[Int]    = identity
  implicit val str: ToInt[String] = _.toInt
}

implicit class ToIntOps[A](val a: A) extends AnyVal {
  def toInt(implicit ti: ToInt[A]): Int = ti.toInt(a)
}

magnet

import scala.language.implicitConversions
import Predef.{augmentString => _, _} // to avoid implicit ambiguity

class SimpleNumber(val numb: Int) {
  def sum(n: ToInt): Int = numb + n.toInt()
}

trait ToInt {
  def toInt(): Int
}
object ToInt {
  implicit def fromInt(i: Int):    ToInt = () => i
  implicit def fromStr(s: String): ToInt = () => s.toInt
}

请注意,您不必导入 ToInt