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

时间:2015-09-20 13:36:59

标签: scala

我对特质构造函数顺序有疑问。

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。

但据我所知,特质构造函数的顺序是从左到右。

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

Logged, ConsoleLogger, TimestampLogger, ShortLogger.

为什么首先执行ShortLogger.log?

2 个答案:

答案 0 :(得分: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

因此,在此示例中,第一次调用log将触发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 :(得分: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)

}

要了解我创建的行为 acct1 acc2

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

同样的方法可以应用于 acct4