关于特征构造函数顺序的scala

关于特征构造函数顺序的scala,scala,Scala,我有一个关于顺序的问题 class Account(initialBalance: Double) extends ConsoleLogger { private var balance = initialBalance def withdraw(amount: Double) = { if (amount > balance) log("Insufficient funds") else { balance -= amount; bala

我有一个关于顺序的问题

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)
}
在acct1中,第一个是ShortLogger.log,第二个是TimestampLogger.log。 在acct2中,第一个是TimestampLogger.log,第二个是ShortLogger.log

但正如我所知,特征构造器的顺序是从左到右

所以acct1的特征构造函数顺序是:

Logged, ConsoleLogger, TimestampLogger, ShortLogger.
为什么先执行ShortLogger.log?

实际上,这都是关于

定义5.1.2设C为一类,其模板C1为。。。使用Cn{stats}。C,L(C)的线性化定义如下:L(C)=C,L(Cn)+:…+:L(C1)

Here+:表示右操作数的元素替换左操作数的相同元素的串联

你可以检查这个问题,答案很清楚。基本上,这个想法是为了弄清楚首先调用哪个方法,必须对类进行线性化。第一个例子是:

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
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
线性化需要删除除最后一个以外的所有重复项:

L(Account) = Account + ShortLogger + TimestamLogger + ConsoleLogger + Logged
因此,在本例中,对log的第一个调用将触发ShortLogger中定义的调用,然后是TimestamLogger,依此类推。第二个例子是:

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
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的方法。这回答了你的问题吗?

这与

要了解我创建的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
注:应用acct1时,acct2中的重复项将被删除

引擎盖下:

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

同样的方法也适用于会计科目4,这取决于线性化规则:。在我看来,它们是Scala中最疯狂的部分,还有隐含的解析规则。一旦您完成了层次结构的线性化,那么“当这些类和特征中的任何一个通过super调用一个方法时,调用的实现将是线性化右侧的第一个实现。”