如果def名称为toString

如果def名称为toString

本文介绍了如果def名称为toString,则Scala隐式def不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此代码无法编译:

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

以上代码无法编译,并出现以下错误:

Above code fails to compile with following error:

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

但是,这段代码可以编译

But, this code compiles

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

为什么隐式定义的名称很重要?

注意:如果将该方法命名为 equals ,则它也不起作用,但是如果将其命名为 hashCode clone 等,则该方法有效.

Note:If the method is named equals, it also does not work but it works if it is named hashCode or clone etc.

推荐答案

这里的问题不是 toString Foo 上重载,而另一个(现在已删除的答案)(您可以尝试类似地重载 asString 并起作用),这是您导入的 toString toString 封闭类(在您的情况下,是由REPL组成的一些合成对象).

The problem here isn't that toString is overloaded on Foo, as one of the other (now-deleted) answers argues (you can try overloading asString similarly and it'll work), it's that the toString you're importing collides with the toString of the enclosing class (in your case some synthetic object made up by the REPL).

我认为下面的无隐式示例(也没有使用诸如 toString 之类的内置"方法名称)更清楚地显示了该问题:

I think the following implicit-free examples (which also don't use "built-in" method names like toString) show the issue a little more clearly:

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

这将使用来自 Bar asString ,即使您可能会认为导入的优先级:

This will use the asString from Bar, even though you might think the imported one would take precedence:

scala> Demo
this is the one from Bar!
res1: Demo.type = Demo$@6987a133

实际上,即使参数不对齐,它也会使用 Bar 中的定义:

In fact it'll use the definition from Bar even if the arguments don't line up:

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

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

Now we can make this look more like your original code:

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

由于相同的原因,此操作失败并显示相同的错误:出于相同的原因:导入的隐式转换被封闭类中具有相同名称的定义隐藏.

This fails with the same error you saw, for the same reason: the imported implicit conversion is hidden by the definition with the same name in the enclosing class.

您询问了以下内容:

隐式名称始终具有重要意义.这就是语言工作的方式.例如:

Names of implicits matter all the time. That's just the way the language works. For example:

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) + ""
                     ^

我们所做的是定义一个隐式值,该值在 scala.Predef 中隐藏 any2stringadd 隐式转换.(是的,这很恐怖.)

What we've done is defined an implicit value that hides the any2stringadd implicit conversion in scala.Predef. (Yes, this is kind of terrifying.)

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

I think there's probably a compiler bug here, at least as far as the error message is concerned. If you change things up just a little bit in my second version above, for example:

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

...您会收到一条更合理的消息:

…you get a much more reasonable message:

<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 在REPL中运行代码:

In my view this is almost certainly the kind of thing the compiler should tell you in your original case. I'm also not sure why the hidden implicit is considered for the conversion at all, but it is, as you can tell if you run your code in a REPL with -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)
           ^

所以看起来隐式性正在消除其他 toString ?老实说,我不知道这里发生了什么,但我想90%的人肯定这是一个错误.

So it looks like the implicitness is rubbing off on the other toString? To be honest I have no idea what's happening here, but I'm like 90% sure it's a mistake.

这篇关于如果def名称为toString,则Scala隐式def不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-18 13:06