I encountered the following code in JAXMag's Scala special issue:
package com.weiglewilczek.gameoflife
case class Cell(x: Int, y: Int) {
override def toString = position
private lazy val position = "(%s, %s)".format(x, y)
}
Does the use of lazy val
in the above code provide considerably more performance than the following code?
package com.weiglewilczek.gameoflife
case class Cell(x: Int, y: Int) {
override def toString = "(%s, %s)".format(x, y)
}
Or is it just a case of unnecessary optimization?
One thing to note about lazy vals is that, while they are only calculated once, every access to them is protected by a double-checked locking wrapper. This is necessary to prevent two different threads from attempting to initialize the value at the same time with hilarious results. Now double-checked locking is pretty efficient (now that it actually works in the JVM), and won't require lock acquisition in most cases, but there is more overhead than a simple value access.
Additionally (and somewhat obviously), by caching the string representation of your object, you are explicitly trading off CPU cycles for possibly large increases in memory usage. The strings in the "def" version can be garbage-collected, while those in the "lazy val" version will not be.
Finally, as is always the case with performance questions, theory-based hypotheses mean nearly nothing without fact-based benchmarking. You'll never know for sure without profiling, so might as well try it and see.
toString
can be directly overriden with a lazy val
.
scala> case class Cell(x: Int, y: Int) {
| override lazy val toString = {println("here"); "(%s, %s)".format(x, y)}
| }
defined class Cell
scala> {val c = Cell(1, 2); (c.toString, c.toString)}
here
res0: (String, String) = ((1, 2),(1, 2))
Note that a def
may not override a val
-- you can only make members more stable in the sub class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With