【问题标题】:How does the Scala compiler resolve an implicit conversionScala 编译器如何解决隐式转换
【发布时间】:2016-01-05 17:57:56
【问题描述】:

昨天我遇到了隐式转换的问题。将其放入导入范围:

object FooConversions { implicit def toString(foo: Foo): String = foo.toString }

给了我编译错误。花了一段时间才发现调用函数toString 是罪魁祸首。我没有花太多时间进一步调查,但我的猜测是它与 Object 中的 toString 方法冲突?编译器如何处理这样的隐式转换?

scala> case class Foo(name: String)
defined class Foo

scala> object FooConversions {
     |   implicit def toString(foo: Foo): String = foo.toString
     | }

defined object FooConversions

scala> import FooConversions._
import FooConversions._

scala> val x: String = Foo("bob")
<console>:16: error: type mismatch;
 found   : Foo
 required: String
       val x: String = Foo("bob")

【问题讨论】:

  • 这个问题有一个很好的答案,关于 scala 在哪里寻找implicits stackoverflow.com/questions/5598085/…
  • 你当然可以通过给FooConversions中的方法取另一个名字来解决这个问题,例如fooToString

标签: scala


【解决方案1】:

这是由于成员阴影造成的。导入的toString 在您期望发生隐式转换的范围内不可用。让我们看看它是如何工作的......

首先让我们检查一下隐式是否有效:

scala> object FooConversions {
     |   implicit def toString(foo: Foo): String = foo.toString
     |
     |   val x: String = Foo("bob")
     | }
defined object FooConversions

是的,它在原始/定义范围内。

现在我们试试看导入的方法toString是否可用:

object FooConversions {
  implicit def toString(foo: Foo): String = foo.toString
}

import FooConversions._

val y: String = toString(Foo("bob"))
<console>:28: error: too many arguments for method toString: ()String
       val y: String = toString(Foo("bob"))

它不可用 - 它被封闭对象的 toString 遮蔽。在 REPL 中,用于编译 Scala 的代码将所有内容都放在某个魔术对象中。在现实生活中,代码将驻留在某个对象/类中。

尝试显式调用toString 以检查它是否可能工作:

scala> val y: String = FooConversions.toString(Foo("bob"))
y: String = Foo(bob)

这里显然没有问题。

因此解决方案是 (1) 将 toString 重命名为其他无法隐藏的名称:

scala> import scala.language.implicitConversions
import scala.language.implicitConversions

scala> case class Foo(name: String)
defined class Foo

scala> object FooConversions {
     |   implicit def toStr(foo: Foo): String = foo.toString
     | }
defined object FooConversions

scala> import FooConversions._
import FooConversions._

scala> val x: String = Foo("bob")
x: String = Foo(bob)

或 (2) 重新定义一个隐式(糟透了):

scala> import scala.language.implicitConversions
import scala.language.implicitConversions

scala> case class Foo(name: String)
defined class Foo

scala> object FooConversions {
     |   implicit def toString(foo: Foo): String = foo.toString
     | }
defined object FooConversions

scala> implicit def toStr(foo: Foo) = FooConversions.toString(foo)
toStr: (foo: Foo)String

scala> val x: String = Foo("bob")
x: String = Foo(bob)

附言这不是隐式查找或隐式范围的问题,而是导入成员的遮蔽/隐藏问题。

【讨论】:

  • 很好的答案,你知道它为什么会被遮住吗?原来的toString()方法不带任何参数,所以这不应该算重载吗?
  • 很高兴我能帮上忙。当您通过def 或通过继承定义成员时会发生重载,在这种情况下不会像object FooConversions { implicit def toString } 那样发生冲突 - 有2 个toString 方法。当我们导入成员时,我们从外部范围引用它们,而内部范围总是通过隐藏外部范围名称来“获胜”。我认为 Scala 首先进行名称解析,删除我们的 toString,然后才解析隐式,因此无法找到隐式转换。
猜你喜欢
  • 2019-09-04
  • 1970-01-01
  • 1970-01-01
  • 2014-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-22
相关资源
最近更新 更多