为什么这个简单的隐式stringToInt函数会导致堆栈溢出?

时间:2013-12-12 10:40:37

标签: scala implicit scala-2.10

如果我定义一个简单的stringToInt函数并将其存储为val,那么一切都按预期工作,例如。

scala> def stringToInt1: (String => Int) = _.toInt
stringToInt1: String => Int

scala> stringToInt1("1")
res0: Int = 1

但是,如果我然后隐含,则会导致堆栈溢出:

scala> implicit def stringToInt2: (String => Int) = _.toInt
stringToInt2: String => Int

scala> stringToInt2("1")
java.lang.StackOverflowError
at .stringToInt2(<console>:7)
at $anonfun$stringToInt2$1.apply(<console>:7)
at $anonfun$stringToInt2$1.apply(<console>:7)
...

起初我怀疑这是因为下划线没有解决我的预期,但事实并非如此,因为这种隐式val的风格适用于以下简单函数:

scala> implicit def plusTwo: (Int => Int) = _ + 2
plusTwo: Int => Int

scala> plusTwo(2)
res2: Int = 4

如果我明确定义参数,则没有堆栈溢出:

scala> implicit def stringToInt3(s: String) = s.toInt
stringToInt3: (s: String)Int

scala> stringToInt3("1")
res3: Int = 1

(如果自己尝试这个并且最后一个案例堆栈溢出,请重新启动scala控制台并重做最后一步)

所以我的问题是,为什么原始的隐含不能正确解析?

修改

好在这里深入挖掘,似乎问题在于从String到StringOps的隐式转换。如果我们减少它,它工作正常:

scala> import scala.collection.immutable.StringOps
import scala.collection.immutable.StringOps

scala> implicit def stringToInt4: (String => Int) = new StringOps(_).toInt
stringToInt4: String => Int

scala> stringToInt4("1")
res4: Int = 1

但为什么隐式转换会导致问题?

2 个答案:

答案 0 :(得分:9)

添加其他回复。

toInt上有没有 String方法。 Scala必须找到一个隐式转换,它将产生一个具有toInt方法的类型。

通常StringOps转化提供此toInt。 但是,Int 还有toInt,因此scala会从String => Int中找到您的转化,并确定它优先于StringOps转化,因此适用它是递归的。

这就是StringToInt4工作的原因,因为您明确地告诉编译器您想要什么转换。也许你可以把它写成:implicit def stringToInt5: (StringOps => Int) = _.toInt或检查implicits是如何解决的,以及如何优先于另一个。

答案 1 :(得分:0)

请注意你在这里做的事情:

scala> def stringToInt1: (String => Int) = _.toInt
stringToInt1: String => Int

您正在定义方法(使用def),该方法不带任何参数,并从String返回函数Int 。你是故意这样做的,还是因为你不完全理解语法?

您可以改为创建val

val stringToInt1: (String => Int) = _.toInt

现在,stringToInt1val,其中包含将String转换为Int的功能。

使用stringToInt2,Scala会以递归方式应用此方法。我不知道为什么在stringToInt2而不是plusTwo的情况下会这样做。

请注意,stringToInt3stringToInt1stringToInt2不同。这是一个采用String并返回Int的方法,而不是不带参数的方法,并将函数从String返回到Int