最近,出于性能原因,我决定将项目的代码库移植到Scala,但就在我刚开始的时候,我被一个我不理解的错误阻止了。这是导致错误的最小代码量:
class Foo[A](var x: A) {
def +(other: Foo[A]) = new Foo[A](this.x + other.x)
}
错误本身:
test.scala:2: error: type mismatch;
found : A
required: String
def +(other: Foo[A]) = new Foo[A](this.x + other.x)
^
环顾四周,我在论坛上发现了一些关于类似错误的帖子,这些错误显然是由Scala将模板类型隐式转换为字符串(?(引起的。
如注释中所述,编译器没有足够的关于类型A
的信息来了解如何+
。
出现令人困惑的错误消息的原因是编译器"知道"所有内容都有toString()
方法,而类型String
有+
方法。那么,为什么它不将两者一起转换为类型String
和+
呢?这是因为A
到String
的转换是一种隐式转换,编译器不会为了解析表达式而进行多个隐式转换。
因此CCD_ 10错误。编译器说:"为了解析+
方法,我已经将第一个A
转换为String
,但现在我已经这样做了,我不能在第二个A
元素上再这样做了。">
对此有几种不同的方法。这是一个。
class Foo[A:Numeric](var x: A) {
def +(other: Foo[A]) =
new Foo[A](implicitly[Numeric[A]].plus(this.x, other.x))
}
CCD_ 15仅限于在CCD_ 16类型类中找到的类型。要将两个A
添加在一起,请将Numeric[A]
的实现从隐式作用域中拉出,并调用其plus()
方法。
这里的问题是this.x + other.x
中的加号方法不是Foo[A]中定义的加号方法。它来自A
。并且由于A
仍然是未定义的,所以可以是Any
。和往常一样,编译器将寻找一种方法来进行编译,在这种情况下,它将找到一个允许this.x
调用+
方法的转换。它会在范围内并具有的Predef.scala
中找到这一点
implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
因此,如果this.x
是能够连接that.x
的String
,那么它也应该是字符串。事实并非如此。
你可以办理入住手续https://github.com/scala/scala/blob/706ef1b291134a5e5bce2275df2c222261f73451/src/library/scala/Predef.scala#L381