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

问题描述 投票:3回答:2

此代码无法编译:

object Foo {
  implicit def toString(i: Int): String = i.toString      
  def foo(x: String) = println(x)
  foo(23)
}    

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

error: type mismatch;
found   : scala.this.Int(23)
required: String
  foo(23)

但是,这段代码编译

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

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

注意:如果该方法名为equals,它也不起作用,但如果它被命名为hashCodeclone等它可以工作。

scala implicit
2个回答
12
投票

这里的问题不是toStringFoo上超载,因为其他(现在删除的)答案之一争论(你可以尝试类似地重载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))
}

这将使用asString中的Bar,即使您可能认为导入的scala> Demo this is the one from Bar! res1: Demo.type = Demo$@6987a133 优先:

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)) } 中的定义,即使参数没有对齐:

<pastie>:25: error: no arguments allowed for nullary method asString: ()String
  println(asString(324))
                   ^

这无法编译:

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))
}

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

implicit def

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

脚注1

你问了以下问题:

为什么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) + "" ^ 的名称应该重要?

隐含的名称一直很重要。这就是语言的工作方式。例如:

any2stringadd

我们所做的是定义了一个隐含的值,它隐藏了scala.Predef中的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)) } } 隐式转换。 (是的,这有点可怕。)

脚注2

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

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

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

-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) ^ 的REPL中运行你的代码:

toString

所以它看起来隐含在其他object Foo { implicit def toString(i: Int): String = i.toString } import Foo.toString 上?说实话,我不知道这里发生了什么,但我确信90%肯定这是一个错误。


2
投票

不确定这是否算作答案(可能是对编译器内部有更多了解的人可以给出更详细的解释),但是在用你的代码玩了一段时间之后我发现了一些东西,我认为这是错误的根源。

鉴于:

val s: String = 10

然后:

toString

生产:

:10:警告:通过类Object中方法toString的定义永久隐藏导入的`toString' import Foo.toString

我认为这意味着,隐式转换被隐藏,因为它的名称与java.langObject(和scala.Any)中定义的通用implicit val int2str: Int => String = Foo.toString val s: String = 10 // s: String = 10 方法相冲突。


Curios足够了,这个有用。

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.