如果def名称为toString,则Scala隐式def不起作用

pat*_*rit 4 scala implicit

此代码无法编译:

object Foo {
  implicit def toString(i: Int): String = i.toString      
  def foo(x: String) = println(x)
  foo(23)
}    
Run Code Online (Sandbox Code Playgroud)

上面的代码无法编译,但有以下错误:

error: type mismatch;
found   : scala.this.Int(23)
required: String
  foo(23)
Run Code Online (Sandbox Code Playgroud)

但是,这段代码编译

object Foo {
  implicit def asString(i: Int): String = i.toString      
  def foo(x: String) = println(x)
  foo(23)
}
Run Code Online (Sandbox Code Playgroud)

为什么名称implicit def应该重要?

注意:如果该方法被命名equals,它也不会工作,但它的作品,如果它被命名hashCodeclone

Tra*_*own 12

这里的问题不是toString重载Foo,因为其中一个(现在删除的)答案争论(你可以尝试asString类似的重载并且它会工作),这是toString你导入的碰撞与toString封闭类的碰撞(在您的情况下由REPL组成的一些合成对象).

我认为以下隐式自由示例(也不使用"内置"方法名称toString)显示问题更清楚:

class Foo {
  def asString(i: Int): String = "this is the one from Foo!"
}

class Bar {
  def asString(i: Int): String = "this is the one from Bar!"
}

object Demo extends Bar {
  val instance = new Foo
  import instance._

  println(asString(23))
}
Run Code Online (Sandbox Code Playgroud)

这将使用asStringfrom Bar,即使您可能认为导入的优先级:

scala> Demo
this is the one from Bar!
res1: Demo.type = Demo$@6987a133
Run Code Online (Sandbox Code Playgroud)

事实上,Bar即使参数没有排列,它也会使用定义:

class Foo {
  def asString(i: Int): String = "this is the one from Foo!"
}

class Bar {
  def asString(): String = "this is the one from Bar!"
}

object Demo extends Bar {
  val instance = new Foo
  import instance._

  println(asString(23))
}
Run Code Online (Sandbox Code Playgroud)

这无法编译:

<pastie>:25: error: no arguments allowed for nullary method asString: ()String
  println(asString(324))
                   ^
Run Code Online (Sandbox Code Playgroud)

现在我们可以使它看起来更像您的原始代码:

class Foo {
  implicit def asString(i: Int): String = "this is the one from Foo!"
  def foo(s: String): String = s
}

class Bar {
  def asString(): String = "this is the one from Bar!"
}

object Demo extends Bar {
  val instance = new Foo
  import instance._

  println(foo(23))
}
Run Code Online (Sandbox Code Playgroud)

由于同样的原因,这会因您看到的错误而失败:导入的隐式转换被封闭类中具有相同名称的定义隐藏.

脚注1

你问了以下问题:

为什么名称implicit def应该重要?

implicits的名字有关系的所有时间.这就是语言的工作方式.例如:

scala> List(1, 2, 3) + ""
res0: String = List(1, 2, 3)

scala> trait Garbage
defined trait Garbage

scala> implicit val any2stringadd: Garbage = new Garbage {}
any2stringadd: Garbage = $anon$1@5b000fe6

scala> List(1, 2, 3) + ""
<console>:13: error: value + is not a member of List[Int]
       List(1, 2, 3) + ""
                     ^
Run Code Online (Sandbox Code Playgroud)

我们所做的是定义了隐藏any2stringadd隐式转换的隐含值scala.Predef.(是的,这有点可怕.)

脚注2

我认为这里可能存在编译器错误,至少就错误消息而言.如果你在我上面的第二个版本中稍微改变一下,例如:

class Foo {
  def asString(i: Int): String = "this is the one from Foo!"
}

class Bar {
  def asString(): String = "this is the one from Bar!"
}

object Demo extends Bar {
  def test(): Unit = {
    val instance = new Foo
    import instance._

    println(asString(23))
  }
}
Run Code Online (Sandbox Code Playgroud)

...你得到一个更合理的信息:

<pastie>:26: error: reference to asString is ambiguous;
it is both defined in class Bar and imported subsequently by
import instance._
    println(asString(23))
            ^
Run Code Online (Sandbox Code Playgroud)

在我看来,这几乎可以肯定是编译器应该在原始案例中告诉你的事情.我也不确定为什么隐藏的隐含被考虑用于转换,但是,正如你可以告诉你是否在REPL中运行你的代码-Xlog-implicits:

scala> foo(23)
<console>:16: toString is not a valid implicit value for Int(23) => String because:
no arguments allowed for nullary method toString: ()String
       foo(23)
           ^
Run Code Online (Sandbox Code Playgroud)

所以看起来隐含性正在另一方面消失toString?说实话,我不知道这里发生了什么,但我确信90%肯定这是一个错误.