I've encountered a somewhat bizzar behavior in objects that extends App
. take a look at the following REPL commands:
scala> object A extends App {val x = "I am null"}
defined module A
scala> object B {val x = "I am a string"}
defined module B
scala> A.x
res0: java.lang.String = null
scala> B.x
res1: java.lang.String = I am a string
well, this is a bit weird... but it gets weirder. i then thought the vals in an object
go into some lazy evaluation... so i tried a real lazy val
:
scala> object C extends App {lazy val x = "What am I?"}
defined module C
scala> C.x
res2: java.lang.String = What am I?
so what's happening here? why is a regular val gets a null value?
why does this behavior changes when i use lazy val
?
and what is so special with the App
trait, that makes the regular vals to be unevaluated?
App extends DelayedInit trait. So all statements and all value definitions are moved to delayedInit
method. Lazy val works because it compiles to method.
For example if you decompile this class:
class TestApp extends App{
val test = "I am null"
lazy val testLazy ="I am a string"
}
You will get class with 'lazy method':
public String testLazy()
{
if((bitmap$0 & 1) == 0)
synchronized(this)
{
if((bitmap$0 & 1) == 0)
{
testLazy = "I am a string";
bitmap$0 = bitmap$0 | 1;
}
BoxedUnit _tmp = BoxedUnit.UNIT;
}
return testLazy;
}
and delayedInit method in inner class delayedInit.body
:
public final class delayedInit.body extends AbstractFunction0
implements ScalaObject
{
public final Object apply()
{
$outer.test_$eq("I am null");
return BoxedUnit.UNIT;
}
private final TestApp $outer;
....
So value "I am null" will be assigned to test
field only when delayedInit is called.
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