I would like to explain the differences in the example that I performed in REPL. I find this simple example easier to understand and explain the conceptual differences.
Here I create val result1, the lazy result of val2 and def def3, each of which is of type String.
BUT). Val
scala> val result1 = {println("hello val"); "returns val"} hello val result1: String = returns val
Here println is executed because the value of result1 is calculated here. So, now result1 will always refer to its value ie "returns val".
scala> result1 res0: String = returns val
So now you can see that result1 now refers to its value. Note that the println statement is not executed here because the value for result1 was already calculated when it was executed for the first time. So now, result1 will always return the same value, and the println statement will never be executed again, because the calculation to get the value of result1 has already been completed.
AT). lazy val
scala> lazy val result2 = {println("hello lazy val"); "returns lazy val"} result2: String = <lazy>
As we see here, the println instruction is not executed here, and not a single value has been computed. This is the nature of laziness.
Now, when I refer to result2 for the first time, the println statement will be executed, and the value will be calculated and assigned.
scala> result2 hello lazy val res1: String = returns lazy val
Now, when I turn to result2 again, this time we will only see its value, and the println instruction will not be executed. From now on, result2 will just behave like val and constantly return a cache value.
scala> result2 res2: String = returns lazy val
FROM). Protection
In the case of def, the result must be computed each time result3 is called. This is also the main reason that we define methods as def in scala, because methods must evaluate and return a value each time it is called inside the program.
scala> def result3 = {println("hello def"); "returns def"} result3: String scala> result3 hello def res3: String = returns def scala> result3 hello def res4: String = returns def