如果 def 名称是 toString,则 Scala 隐式 def 不起作用
Scala implicit def do not work if the def name is toString
此代码无法编译:
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
,它也不起作用,但如果它被命名为 hashCode
或 clone
等,它会起作用
不确定这是否算作答案(可能对编译器内部有更多了解的人可以给出更详细的解释),但是在玩了一段时间之后code 我发现了一些东西,我认为这是错误的根源。
鉴于:
object Foo {
implicit def toString(i: Int): String = i.toString
}
import Foo.toString
然后:
val s: String = 10
生产:
:10: warning: imported `toString' is permanently hidden by definition of method toString in class Object
import Foo.toString
我认为这意味着隐式转换被隐藏了,因为它的名称与 java.langObject
(和 scala.Any
)中定义的通用 toString
方法冲突.
够奇葩了,这个有用。
implicit val int2str: Int => String = Foo.toString
val s: String = 10
// s: String = 10
这里的问题不是 toString
在 Foo
上过载,正如其他(现已删除)答案之一所争论的那样(您可以尝试类似地重载 asString
和它会起作用),这是因为您要导入的 toString
与封闭的 class 的 toString
发生冲突(在您的情况下,是由 REPL 组成的一些合成对象)。
我认为以下无隐式示例(也不使用 "built-in" 方法名称,如 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))
}
这将使用 Bar
中的 asString
,尽管您可能认为导入的优先:
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))
}
由于相同的原因,此操作失败并出现与您看到的相同的错误:导入的隐式转换被封闭的 class.
中具有相同名称的定义隐藏了
脚注 1
您问了以下问题:
Why does the name of an implicit def
should matter?
隐含的名称一直很重要。这就是语言的工作方式。例如:
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@5b000fe6
scala> List(1, 2, 3) + ""
<console>:13: error: value + is not a member of List[Int]
List(1, 2, 3) + ""
^
我们所做的是定义一个隐式值,它隐藏 scala.Predef
中的 any2stringadd
隐式转换。 (是的,这有点可怕。)
脚注 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))
}
}
…你得到一个更合理的信息:
<pastie>:26: error: reference to asString is ambiguous;
it is both defined in class Bar and imported subsequently by
import instance._
println(asString(23))
^
在我看来,这几乎肯定是编译器应该在您的原始情况下告诉您的那种事情。我也不确定为什么隐藏的隐式被完全考虑用于转换,但它是,正如你可以判断的那样,如果你 运行 你的代码在 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)
^
所以看起来含蓄正在影响另一个toString
?老实说,我不知道这里发生了什么,但我 90% 确定这是一个错误。
此代码无法编译:
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
,它也不起作用,但如果它被命名为 hashCode
或 clone
等,它会起作用
不确定这是否算作答案(可能对编译器内部有更多了解的人可以给出更详细的解释),但是在玩了一段时间之后code 我发现了一些东西,我认为这是错误的根源。
鉴于:
object Foo {
implicit def toString(i: Int): String = i.toString
}
import Foo.toString
然后:
val s: String = 10
生产:
:10: warning: imported `toString' is permanently hidden by definition of method toString in class Object
import Foo.toString
我认为这意味着隐式转换被隐藏了,因为它的名称与 java.langObject
(和 scala.Any
)中定义的通用 toString
方法冲突.
够奇葩了,这个有用。
implicit val int2str: Int => String = Foo.toString
val s: String = 10
// s: String = 10
这里的问题不是 toString
在 Foo
上过载,正如其他(现已删除)答案之一所争论的那样(您可以尝试类似地重载 asString
和它会起作用),这是因为您要导入的 toString
与封闭的 class 的 toString
发生冲突(在您的情况下,是由 REPL 组成的一些合成对象)。
我认为以下无隐式示例(也不使用 "built-in" 方法名称,如 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))
}
这将使用 Bar
中的 asString
,尽管您可能认为导入的优先:
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))
}
由于相同的原因,此操作失败并出现与您看到的相同的错误:导入的隐式转换被封闭的 class.
中具有相同名称的定义隐藏了脚注 1
您问了以下问题:
Why does the name of an
implicit def
should matter?
隐含的名称一直很重要。这就是语言的工作方式。例如:
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@5b000fe6
scala> List(1, 2, 3) + ""
<console>:13: error: value + is not a member of List[Int]
List(1, 2, 3) + ""
^
我们所做的是定义一个隐式值,它隐藏 scala.Predef
中的 any2stringadd
隐式转换。 (是的,这有点可怕。)
脚注 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))
}
}
…你得到一个更合理的信息:
<pastie>:26: error: reference to asString is ambiguous;
it is both defined in class Bar and imported subsequently by
import instance._
println(asString(23))
^
在我看来,这几乎肯定是编译器应该在您的原始情况下告诉您的那种事情。我也不确定为什么隐藏的隐式被完全考虑用于转换,但它是,正如你可以判断的那样,如果你 运行 你的代码在 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)
^
所以看起来含蓄正在影响另一个toString
?老实说,我不知道这里发生了什么,但我 90% 确定这是一个错误。