这个隐式val如何导致StackOverFlowError?
(削减原始代码,仍导致错误)
object Complicit {
// a class with name, default, and conversion function as implicit val
case class CC[A](name: String, defaultValue: A)(implicit val convert: String => A) {
def getFrom(s: String): A= try {
convert(s)
} catch {
case t: Throwable =>
println("ERROR: %s".format(t)) // just to see the StackOverflowException
defaultValue
}
}
// this works fine
object Works {
val cc1= CC("first", 0.1)(_.toDouble)
}
// this causes java.lang.StackOverflowError due to the implicit
object Fails {
// !!! StackOverFlowError here
implicit val stringToDouble: String => Double= { _.toDouble }
val cc2= CC("second", 0.2)
}
def main(args: Array[String]) {
// this works
println("%s %f".format(Works.cc1.name, Works.cc1.getFrom("2.3")))
// this fails
println("%s %f".format(Fails.cc2.name, Fails.cc2.getFrom("4.5")))
}
}
我是否在做违法行为?
答案 0 :(得分:9)
我相信我可以回答这里发生的事情..它与其他隐式转换以及您刚创建的转换有关。如果你添加这个跟踪,你可以确认通常与哪些堆栈溢出有关 - 一个函数自己重复调用,直到java的堆栈空间崩溃:
implicit val stringsToDouble: String => Double= { x=>println("called inner "+x); x.toDouble }
.... 叫内在4.5 叫内在4.5 叫内在4.5 叫内在4.5 名为inner 4.5ERROR:java.lang.StackOverflowError
我认为发生的事情是这样的 - toDouble不是java字符串的自然功能,而是使用StringOps中的隐式转换(或StringLike,我不是很确定,但它是同一个问题)。
因此,当您调用toDouble时,编译器开始寻找可能包含函数“toDouble”的隐式转换。从理论上讲,它可能是任何由此产生的阶级。
但是 - 如果几次隐式转换可以实现这一目标会发生什么?不幸的是,“Double”还包含功能toDouble,如下所示:
val x = 44.4
x.toDouble
猜猜是什么?这意味着你现在最接近范围的新隐式函数赢得了比赛,并且在圆圈中调用以完成“toDouble” - 有效地尝试将字符串转换为double,以便重复调用toDouble(在Double类上)。我承认这很令人困惑,但证据确实合适。
这是修复..它符合解释并阻止递归调用。
implicit val stringsToDouble: String => Double= { java.lang.Double.parseDouble(_) }