2

因此,我正在阅读《不耐烦》一书中的 Scala,它使用的示例之一是Logger本质上将 a 打印String到某个流的特征。在示例中,它具有将消息打印到的特征ConsoleLogger(extends )和(也是extends )如果长度太长则简单地截断字符串。要更改本书的最大长度,建议使用匿名子类,例如:LoggerstdoutShortLoggerLoggerShortLogger

val acct = new SavingsAccount with ConsoleLogger with ShortLogger {
  val maxLength = 20
}

whereShortLogger是具有抽象maxLength: Int字段的特征,SavingsAccount定义为

class SavingsAccount extends Account with Logged { ... }

这对我来说很有意义(有点)。我假设施工顺序是:

  1. Logger首先构造(因为它是 的超特征ConsoleLogger),
  2. ConsoleLogger
  3. ShortLogger
  4. Account
  5. SavingsAccount.
  6. 然后我们有匿名子类构造,我们在其中定义了 abstract maxLength = 20

然而,在本书的后面,它给出了一个新的Logger子特征:

trait FileLogger extends Logger {
  val filename: String
  val out = new PrintStream(filename)
  def log(msg: String) { out.println(msg); out.flush() }
}

val acct = new SavingsAccont with FileLogger {
  val filename = "myapp.log" // Does not work
}

它说由于施工顺序,它不起作用。他们提出修改:

val acct = new {
  val filename: "myapp.log"
} with SavingsAccount with FileLogger

但是,这个定义似乎与上面的定义相似maxLength,那么在上面的示例和底部示例之间我缺少什么?

4

1 回答 1

5

你的施工顺序完全错误。:-)

首先要构造的是类,然后从左到右遍历特征。这有点复杂——有处理多次继承的特征的线性化规则,但仅此而已。

所以:

  1. 帐户
  2. 已记录
  3. 储蓄账户
  4. 控制台记录器
  5. 短记录器
  6. 匿名子类

现在,对于你的问题:

val acct = new SavingsAccont with FileLogger {
  val filename = "myapp.log" // Does not work
}

val acct = new {
  val filename: "myapp.log"
} with SavingsAccount with FileLogger

Note that the block of code comes first in the second example. That's known as early initialization, and what it does is to apply that initialization before anything else.

于 2012-07-04T22:46:35.877 回答