scala - Field in a trait is not initialized in time -


i can't figure out why field encryptkey in following code not initialised 3 time class constructor called:

trait logger{   println("construction of logger")   def log(msg: string) { println(msg) } }  trait encryptinglogger extends logger {   println("construction of encryptinglogger")   val encryptkey = 3    override def log(msg: string){     super.log(msg.map(encrypt(_, encryptkey)))   }    def encrypt(c: char, key: int) =     if (c islower) (((c - 'a') + key) % 26 + 'a').tochar     else if (c isupper) (((c.toint - 'a') + key) % 26 + 'a').tochar     else c }  class secretagent (val id: string, val name: string) extends logger {   println("construction of secretagent")   log("agent " + name + " id " + id + " created.") }  val bond = new secretagent("007", "james bond") encryptinglogger 

in understanding, linearization of created object be:

secretagent -> encryptinglogger -> logger -> scalaobject 

and construction order goes right left, meaning variable should initialized before constructor of secretagent starts. prinln's tell me different:

scala> val bond = new secretagent("007", "james bond") encryptinglogger construction of logger construction of secretagent agent james bond id 007 created. construction of encryptinglogger bond: secretagent encryptinglogger = $anon$1@49df83b5 

i tried mixin same trait differently:

class secretagent (val id: string, val name: string) extends logger encryptinglogger 

and variable initialized in time:

scala> val bond = new secretagent("007", "james bond") construction of logger construction of encryptinglogger construction of secretagent djhqw mdphv erqg zlwk lg 007 zdv fuhdwhg. bond: secretagent = secretagent@1aa484ca 

so difference between mixing in class definition , mixing in object, explain?

the problem here call method in constructor of class, accesses field of superclass/trait, before super constructor called. easiest way workaround that, make field lazy, because evaluated, when first accessed:

trait logger{   def log(msg: string) { println(msg) } }  trait encryptinglogger extends logger {   lazy val encryptkey = 3    override def log(msg: string){     super.log(msg.map(encrypt(_, encryptkey)))   }    def encrypt(c: char, key: int) =     if (c islower) (((c - 'a') + key) % 26 + 'a').tochar     else if (c isupper) (((c.toint - 'a') + key) % 26 + 'a').tochar     else c }  class secretagent (val id: string, val name: string) extends logger {   log("agent " + name + " id " + id + " created.") }   scala> val bond = new secretagent("007", "james bond") encryptinglogger djhqw mdphv erqg zlwk lg 007 zdv fuhdwhg. bond: secretagent encryptinglogger = $anon$1@4f4ffd2f 

edit:

it becomes clear happens here, when @ decompiled java code for

class foo extends secretagent("007", "james bond") encryptinglogger 

where constructor looks this

public foo() {    super("007", "james bond");   encryptinglogger.class.$init$(this); } 

as can see, first calls super constructor (in case secretagent) calls log method , after calls init method of encryptionlogger. therefore encryptkey still has default value, 0 integers.

if make encryptkey field lazy, getter this:

public int encryptkey() {   return this.bitmap$0 ? this.encryptkey : encryptkey$lzycompute(); } 

and @ first access calls following method set field value:

private int encryptkey$lzycompute() {   synchronized (this) {     if (!this.bitmap$0) {       this.encryptkey = encryptinglogger.class.encryptkey(this);       this.bitmap$0 = true;     }     return this.encryptkey;   } } 

edit2:

to answer question order of constructors, calls constructors in correct order. when create anonymous instance, like

class anonymous extends secretagent encryptinglogger 

in case, constructor of secretagent called first. if extend class secretagent trait, call super constructors first. behaves absolutely expected. if want use traits mixins anonymous instances, have careful initialization order , here helps make fields, accessed in constructors lazy.


Comments

Popular posts from this blog

SPSS keyboard combination alters encoding -

Add new record to the table by click on the button in Microsoft Access -

javascript - jQuery .height() return 0 when visible but non-0 when hidden -