【发布时间】:2011-08-29 19:07:34
【问题描述】:
考虑以下sn-p。 += 不是 java.lang.String 的成员,所以我想有某种隐式转换正在进行。如何找到作用于 String 的此类预定义隐式转换的列表?
scala> var x = "asdf"
x: java.lang.String = asdf
scala> x += x
scala> x
res2: java.lang.String = asdfasdf
【问题讨论】:
考虑以下sn-p。 += 不是 java.lang.String 的成员,所以我想有某种隐式转换正在进行。如何找到作用于 String 的此类预定义隐式转换的列表?
scala> var x = "asdf"
x: java.lang.String = asdf
scala> x += x
scala> x
res2: java.lang.String = asdfasdf
【问题讨论】:
你选择了一个特别糟糕的例子。在某种意义上,+= 是String 的一部分。请参阅java.lang.String 的 Javadoc 上的此评论:
Java 语言为字符串提供了特殊的支持 连接运算符 (
+),并将其他对象转换为 字符串。
您必须查找 Java language specification 才能找到有关它的更多信息 (15.18.1)。但是,Scala 不是 Java,所以 + 也是 Scala language specification (12.3.1) 的一部分。
到目前为止,我说的是+,而不是+=。然而,Scala 有一个特殊的赋值语法糖。如第 6.12.4 节所述,除了<=、>=、!= 和以= 开头的运算符外,任何以等号结尾的运算符符号(参见第 1 章中的“运算符字符”)都将如果它不作为方法存在,则被重新解释。具体来说,
x += 1
将被重新解释为
x = x + 1
无论x 是否为var,都会发生这种情况,因此有时可能会看到错误消息“reassignment to val”。
所以,如您所见,+= 确实是 String 的一部分,通过 Java 规范中的一个异常被复制到 Scala 规范,加上一些语法糖。
这并不意味着java.lang.String 中没有可以通过隐式转换使用的方法。但是,我将把它留给其他答案。如果我是你,我会改变问题中的方法,使其正确。此外,+= 在 Stack Overflow 中是无法搜索到的。
【讨论】:
您需要查看scala.Predef - 在那里定义的所有隐式将始终在范围内(因此您不需要导入它们)。
如果你查看it's source code,你会发现这个部分:
// Strings and CharSequences ------------
...
implicit def augmentString(x: String): StringOps = new StringOps(x)
implicit def unaugmentString(x: StringOps): String = x.repr
...
【讨论】:
StringOps 定义 String 的隐式转换。它与其他有用的东西一起被导入到scala.Predef 的作用域中。
【讨论】:
不特定于String,但 Scala REPL 有一个简洁的功能可以查看所有范围内的隐含:
scala> :implicits
No implicits have been imported other than those in Predef.
scala> :implicits -v
/* 96 implicit members imported from scala.Predef */
/* 66 inherited from scala.Predef */
implicit def Double2double(x: jl.Double): Double
implicit def byte2double(x: Byte): Double
implicit def char2double(x: Char): Double
...
/* 30 inherited from scala.LowPriorityImplicits */
implicit def genericWrapArray[T](xs: Array[T]): mutable.WrappedArray[T]
implicit def wrapBooleanArray(xs: Array[Boolean]): mutable.WrappedArray[Boolean]
implicit def wrapByteArray(xs: Array[Byte]): mutable.WrappedArray[Byte]
...
还值得注意的是,隐式不必在应用范围内。例如,我们可以在伴生对象中定义隐式转换,
case class Foo(s: String)
object Foo { implicit def string2Foo(s: String) = Foo(s.reverse) }
然后应用它,即使它不在范围内,
scala> val f: Foo = "hello"
f: Foo = Foo(olleh)
搜索 Foo 伴随对象以查找隐式,因为目标类型是 Foo。有关详细信息,请参阅 Daniel Sobral 对以下内容的回答:Where does Scala look for implicits?
【讨论】: