我对特质构造函数顺序有疑问。
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?
答案 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)
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