Am I right understanding that
def
is evaluated every time it gets accessed
lazy val
is evaluated once it gets acce
Should point out a potential pitfall in regard to usage of val when working with values not known until runtime.
Take, for example, request: HttpServletRequest
If you were to say:
val foo = request accepts "foo"
You would get a null pointer exception as at the point of initialization of the val, request has no foo (would only be know at runtime).
So, depending on the expense of access/calculation, def or lazy val are then appropriate choices for runtime-determined values; that, or a val that is itself an anonymous function which retrieves runtime data (although the latter seems a bit more edge case)
You are correct. For evidence from the specification:
From "3.3.1 Method Types" (for def
):
Parameterless methods name expressions that are re-evaluated each time the parameterless method name is referenced.
From "4.1 Value Declarations and Definitions":
A value definition
val x : T = e
definesx
as a name of the value that results from the evaluation ofe
.A lazy value definition evaluates its right hand side
e
the first time the value is accessed.
One good reason for choosing def
over val
, especially in abstract classes (or in traits that are used to mimic Java's interfaces), is, that you can override a def
with a val
in subclasses, but not the other way round.
Regarding lazy
, there are two things I can see that one should have in mind. The first is that lazy
introduces some runtime overhead, but I guess that you would need to benchmark your specific situation to find out whether this actually has a significant impact on the runtime performance. The other problem with lazy
is that it possibly delays raising an exception, which might make it harder to reason about your program, because the exception is not thrown upfront but only on first use.
A name qualified by def is evaluated by replacing the name and its RHS expression every time the name appears in the program. Therefore, this replacement will be executed every where the name appears in your program.
A name qualified by val is evaluated immediately when control reaches its RHS expression. Therefore, every time the name appears in the expression, it will be seen as the value of this evaluation.
A name qualified by lazy val follows the same policy as that of val qualification with an exception that its RHS will be evaluated only when the control hits the point where the name is used for the first time
def
defines a method. When you call the method, the method ofcourse runs.
val
defines a value (an immutable variable). The assignment expression is evaluated when the value is initialized.
lazy val
defines a value with delayed initialization. It will be initialized when it's first used, so the assignment expression will be evaluated then.
Yes, but there is one nice trick: if you have lazy value, and during first time evaluation it will get an exception, next time you'll try to access it will try to re-evaluate itself.
Here is example:
scala> import io.Source
import io.Source
scala> class Test {
| lazy val foo = Source.fromFile("./bar.txt").getLines
| }
defined class Test
scala> val baz = new Test
baz: Test = Test@ea5d87
//right now there is no bar.txt
scala> baz.foo
java.io.FileNotFoundException: ./bar.txt (No such file or directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:137)
...
// now I've created empty file named bar.txt
// class instance is the same
scala> baz.foo
res2: Iterator[String] = empty iterator