I can't figure out why the field encryptKey in the following code is not initialised to 3 by the time class constructor is 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 + " with id " + id + " was created.")
}
val bond = new SecretAgent("007", "James Bond") with EncryptingLogger
In my understanding, linearization of the created object would be:
SecretAgent -> EncryptingLogger -> Logger -> ScalaObject
and construction order goes from right to left, meaning that the variable should be already initialized before constructor of SecretAgent starts. But the prinln's tell me different:
scala> val bond = new SecretAgent("007", "James Bond") with EncryptingLogger
Construction of Logger
Construction of SecretAgent
Agent James Bond with id 007 was created.
Construction of EncryptingLogger
bond: SecretAgent with EncryptingLogger = $anon$1@49df83b5
I tried to mixin the same trait differently:
class SecretAgent (val id: String, val name: String) extends Logger with EncryptingLogger
and the variable is 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 what is the difference between mixing in the class definition and mixing in the object, could someone explain?
The problem here is that you call a method in the constructor of your class, that accesses a field of the superclass/trait, before the super constructor is called. The easiest way to workaround that, is to make the field lazy, because it will then be evaluated, when it is 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 + " with id " + id + " was created.")
}
scala> val bond = new SecretAgent("007", "James Bond") with EncryptingLogger
Djhqw Mdphv Erqg zlwk lg 007 zdv fuhdwhg.
bond: SecretAgent with EncryptingLogger = $anon$1@4f4ffd2f
edit:
It becomes clear what happens here, when we look at the decompiled java code for
class Foo extends SecretAgent("007", "James Bond") with EncryptingLogger
where the constructor looks like this
public Foo() { 
  super("007", "James Bond");
  EncryptingLogger.class.$init$(this);
}
As you can see, it first calls the super constructor (in this case SecretAgent) which calls the log method and after that it calls the init method of the EncryptionLogger. Therefore the encryptKey still has its default value, which is 0 for integers. 
If you now make the encryptKey field lazy, the getter will look like this:
public int encryptKey() {
  return this.bitmap$0 ? this.encryptKey : encryptKey$lzycompute();
}
and at first access it calls the following method to set the field to its 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 your question about the order of constructors, it actually calls the constructors in the correct order. When you create an anonymous instance, it actually is like
class Anonymous extends SecretAgent with EncryptingLogger
in this case, the constructor of SecretAgent will be called first. If you extend the class SecretAgent with the trait, it will call the super constructors first. It behaves absolutely as expected. So if you want to use traits as mixins for anonymous instances, you have to be careful about the initialization order and here it helps to make fields, that are likely to be accessed in constructors lazy.
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