In the Laziness section of 'Effective Scala', it says:
Fields in scala are computed by need when val is prefixed with lazy. Because fields and methods are equivalent in Scala (lest the fields are private[this])
What does it mean by 'fields' and 'methods' being equivalent? Isn't it a rather strong statement?
Well it simply means that you can define an abstract val
by a def
and an abstract def
by a val
.
For example
trait Server {
def port:Int
}
has an abstract function, namely port
. You can for sure implement (or define or override) this with a def
, like this
object DefaultServer extends Server {
override def port: Int = 80
}
But in this case every access to port
will cause a function application (or a method call) which is simply unnecessary. For this simple reason Scala gives us the possibility of implementing the abstract def
with a value:
object DefaultServer extends Server {
override val port: Int = 80
}
The same applies to abstract values. You can define abstract values with the same syntax:
trait Server {
val port: Int
}
And you can override them with a def
:
object DefaultServer extends Server {
override def port: Int = 80
}
Stability
You may wonder what will happen if you override an abstract val
with a def
which gives you a different value each time you call it. Will you get the first computed value because the item is a val
or you will get a different value each time you call it because the actual implementation is a def
.
For example:
object DefaultServer extends Server {
override def port: Int = scala.util.Random.nextInt()
}
Fortunately Scala compiler detects this and throws the following error:
error: overriding value a in trait Server of type Int;
method a needs to be a stable, immutable value
override def port:Int = scala.util.Random.nextInt()
Laziness
When it comes to laziness this uniform behavior (treating fields and methods in the same way) is very useful.
First note that a lazy
abstract value does not exist, i.e. you can not define an abstract val
as lazy
.
Implementing an abstract def
by a lazy val
, on the other hand, is perfectly legal and useful. The lazy
value will be computed only on the first call and memoized (cached and used for future calls).