2013-05-19 25 views
3

我想不通,爲什麼在下面的代碼字段encryptKey不是由時間類的構造函數初始化到3被稱爲:場未初始化的時候

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 

在我的理解,創建的對象的線性化將是:

SecretAgent -> EncryptingLogger -> Logger -> ScalaObject 

和施工順序變爲從右到左,這意味着變量應SecretAgent的構造開始前已經初始化。但prinln的告訴我不同​​:

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 = [email protected] 

我試圖混入同一性狀不同:

class SecretAgent (val id: String, val name: String) extends Logger with EncryptingLogger 

和變量初始化時間:

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 = [email protected] 

那麼有什麼區別在混合類定義和混合對象之間,可以有人解釋一下嗎?

回答

2

這裏的問題是,你調用類的構造方法,訪問超/特徵的領域,前超級構造函數被調用。要解決最簡單的方法,就是讓外地偷懶,因爲它會再進行評估,當第一次訪問它:

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 = [email protected] 

編輯

很清楚這裏發生了什麼,當我們看一下反編譯Java代碼

class Foo extends SecretAgent("007", "James Bond") with EncryptingLogger 

在構造看起來像這樣

public Foo() { 
    super("007", "James Bond"); 
    EncryptingLogger.class.$init$(this); 
} 

正如您所看到的,它首先調用超級構造函數(在本例中爲SecretAgent),它調用日誌方法,然後調用EncryptionLoggerinit方法。因此encryptKey仍然有其默認值,即整數的0

如果你現在做的encryptKey場懶,吸氣將是這樣的:

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

,並在第一次訪問它調用下面的方法將字段設置爲它的價值:

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

編輯2

要回答關於構造函數順序的問題,它實際上調用con正確順序的結構。當你創建一個匿名的情況下,它實際上是像

class Anonymous extends SecretAgent with EncryptingLogger 
在這種情況下

SecretAgent構造函數將首先被調用。如果將類SecretAgent擴展爲特徵,則會首先調用超級構造函數。它的行爲完全如預期。所以如果你想使用traits作爲匿名實例的混合,你必須小心初始化順序,這裏有助於創建可能在構造函數中被懶惰訪問的字段。

+0

嗨,你能解釋爲什麼超類在構造子類之前不構造嗎?根據線性化規則,EncryptingLogger應該首先被初始化。 – damluar

+0

我更新了問題 – damluar

+0

更新了我的答案 – drexin

2

使用extends

class SecretAgent (val id: String, val name: String) extends EncryptingLogger 
val bond = new SecretAgent("007", "James Bond"); 

在上述情況下,從所述性狀特性被添加到通過繼承SecretAgent類。簡單的舊直接繼承。所以當你嘗試實例化一個對象時,「鉤子」已經被創建。

使用with

雖然它產生在這種情況下類似的輸出,withextends是不同的運營商。 with用於mixin,這意味着您的特徵的屬性以與使用繼承不同的方式添加到對象。例如,如果您沒有爲每個實例化添加with EncryptionLogger,則並非所有的SecretAgent對象甚至都會有encryptKey

要隱含指定所有SpecialAgent對象從EncryptingLogger繼承使用extends

定時

即使經過仔細重新挖過馬丁·奧德斯基的書(第217 - 221),沒有混入懶惰evaled或者被應用到混入某種形式的延期解析提及。我也跑了兩個你的例子,他們都產生相同的輸出:

println(bond.encryptKey); // 3 
+0

當你運行第一個例子(混入對象)時,你會得到不同的構造順序:記錄器 - > SecretAgent - > EncryptinAgent。我改變了代碼並添加了printlns來顯示這個 – damluar