2015-09-20 39 views
0

我對特性構造函數的順序有疑問。關於特質構造函數的命令的訂單

class Account(initialBalance: Double) extends ConsoleLogger { 
    private var balance = initialBalance 

    def withdraw(amount: Double) = { 
    if (amount > balance) log("Insufficient funds") 
    else { 
     balance -= amount; 
     balance 
    } 
    } 

    def getAccount = balance 
} 

trait Logged { 
    def log(msg: String): Unit = {} 
} 

trait ConsoleLogger extends Logged { 
    override def log(msg: String): Unit = { 
    println(msg) 
    } 
} 

trait TimestampLogger extends Logged { 
    override def log(msg: String) { 
    super.log(new Date() + " " + msg) 
    } 
} 

trait ShortLogger extends Logged { 
    val maxLength = 15 

    override def log(msg: String) { 
    super.log(if (msg.length <= maxLength) msg else msg.substring(0, maxLength - 3) + "...") 
    } 
} 

object test9 extends App { 
    val acct1 = new Account(100) with ConsoleLogger with TimestampLogger with ShortLogger 
    val acct2 = new Account(100) with ConsoleLogger with ShortLogger with TimestampLogger 

    acct1.withdraw(500) 
    acct2.withdraw(500) 
} 

結果是:

> Sun Sep 20 21:25:57 CST 2015 Insufficient... 
> Sun Sep 20 2... 

所以在acct1,弗里斯特是ShortLogger.log,第二個是TimestampLogger.log。 在acct2中,第一個是TimestampLogger.log,第二個是ShortLogger.log

但是正如我知道的,trait構造函數的順序是從左到右。 因此,acct1的特質構造函數順序是Logged,ConsoleLogger,TimestampLogger,ShortLogger。 爲什麼要首先執行ShortLogger.log?

+0

這是線性化ru les:https://www.artima.com/pins1ed/traits.html。在我看來,他們是Scala中最瘋狂的部分,同時還有隱含的解析規則。一旦確定了層次結構的線性化,那麼「當這些類和特徵中的任何一個通過super調用一個方法時,被調用的實現將是線性化的第一個實現。」 –

回答

1

實際上,它全部是關於linearization

定義5.1.2設C是模板C1與Cn {stats}的類。 C,L(C)的線性化定義如下:L(C)= C,L(Cn)+:... +:L(C1)

這裏+:表示串聯,操作數替換左操作數的相同元素。

你可以檢查這個問題,answer非常清楚。基本上,這個想法是,爲了弄清楚首先調用哪種方法,你必須線性化你的類。因此,與第一個例子:

new Account(100) with ConsoleLogger with TimestampLogger with ShortLogger 

L(Account) = Account + L(ShortLogger) + L(TimestamLogger) + L(ConsoleLogger) 
L(Account) = Account + ShortLogger + Logged + TimestamLogger + Logged + ConsoleLogger + Logged 

線性化要求刪除所有副本除了最後一個:

L(Account) = Account + ShortLogger + TimestamLogger + ConsoleLogger + Logged 

所以在這個例子中,記錄第一次通話將觸發ShortLogger定義的,然後TimestamLogger等等。第二個例子:

new Account(100) with ConsoleLogger with ShortLogger with TimestampLogger 

L(Account) = Account + L(TimestampLogger) + L(ShortLogger) + L(ConsoleLogger) 
L(Account) = Account + TimestampLogger + Logged + ShortLogger + Logged + ConsoleLogger + Logged 
L(Account) = Account + TimestampLogger + ShortLogger + ConsoleLogger + Logged 

這裏,第一種方法叫TimestampLogger,然後是ShortLogger和ConsoleLogger。這是回答你的問題嗎?

1

它涉及到Linerization

object Main extends App { 
    val acct1 = new SavingsAccount with ShortLogger 
    val acct2 = new SavingsAccount with TimeStampLogger 


    val acct3 = new SavingsAccount with TimeStampLogger with ShortLogger 
    val acct4 = new SavingsAccount with ShortLogger with TimeStampLogger 

    acct1.withdraw(100) 
    acct2.withdraw(100) 

    acct3.withdraw(100) 

} 

要理解我創建acct1ACC2行爲:

acct1.withdraw(100) // => Insufficient... 

acct2.withdraw(100) // => Wed Apr 05 11:59:09 EEST 2017 Insufficient amount 

acct3.withdraw(100) // => Wed Apr 05 11:59:09 EEST 2017 Insufficient... 

acct3行爲:/ * +:表示連接*/

acct3 = acct1 +: acct2 

注:重複acct2除去acct1在引擎蓋下施加

L(Account) = Account + L(ShortLogger) + L(TimestamLogger) + L(ConsoleLogger) 
L(Account) = Account + ShortLogger + Logged + TimestamLogger + Logged + ConsoleLogger + Logged 

相同方法可應用於爲acct4