Scala - 特质成员初始化:使用特质修改类成员

3
也许标题不太清晰,这是我的问题。
假设我有一个特性,它定义了一系列配置参数的应用程序。这些参数包含在一个Map中,其中一些具有默认值。
trait ConfApp {
  val dbName: String
  lazy val conf: scala.collection.mutable.Map[String, Any] = scala.collection.mutable.Map("db" -> dbName, "foo" -> "bar")
}

所以我可以按照以下方式创建自定义应用程序:
class MyApp extends ConfApp {
  override val dbName = "my_app_db"

  // print app configuration parameters
  println(conf)

  def add() = {...}
  ...
}

val M1 = new Myapp    // Map(db -> my_app_db, foo -> bar)

我希望创建其他特征,设置一些其他配置参数。换句话说,我希望能够做到像这样:

class MyApp2 extends ConfApp with LogEnabled {
  override val dbName = "my_app2_db"
  // print app configuration parameters
  println(conf)

  def add() = {...}
  ...
}

val M2 = new Myapp2    // Map(db -> my_app_db, foo -> bar, log -> true)

到目前为止,我已经完成了以下工作:

trait LogEnabled {
  val conf: scala.collection.mutable.Map[String, Any]
  conf("log") = true
}

trait LogDisabled {
  val conf: scala.collection.mutable.Map[String, Any]
  conf("log") = false
}

trait ConfApp {
  val dbName: String
  lazy val conf: scala.collection.mutable.Map[String, Any] = scala.collection.mutable.Map("db" -> dbName, "foo" -> "bar")
}

class MyApp extends ConfApp {
  val dbName = "my_app_db"
  println(conf)
}

class MyApp2 extends ConfApp with LogDisabled {
  val dbName = "my_app_db"
  println(conf)
}

val M = new MyApp         // Map(db -> my_app_db, foo -> bar)
val M2 = new MyApp2       // Map(log -> false, foo -> bar, db -> null)

但是,正如您在M2中所看到的,db参数为null。我不明白我做错了什么。

说实话,我并不喜欢可变Map的这种方法,但我还没有找到更好的解决方案。


1
啊,当混入特质时处理初始化问题的美妙。 - Ende Neu
2个回答

3
您仍然可以以以下方式使用不可变的 Map:
scala> trait ConfApp {
     |   val dbName: String
     |   def conf: Map[String, Any] = Map("db" -> dbName, "foo" -> "bar")
     | }
defined trait ConfApp

scala> trait LogEnabled extends ConfApp {
     |   override def conf = super.conf.updated("log", true)
     | }
defined trait LogEnabled

scala> trait LogDisabled extends ConfApp {
     |   override def conf = super.conf.updated("log", false)
     | }
defined trait LogDisabled

scala> class MyApp extends ConfApp {
     |   val dbName = "my_app_db"
     |   println(conf)
     | }
defined class MyApp

scala> class MyApp2 extends ConfApp with LogDisabled {
     |   val dbName = "my_app_db2"
     |   println(conf)
     | }
defined class MyApp2

scala> new MyApp
Map(db -> my_app_db, foo -> bar)
res0: MyApp = MyApp@ccc268e

scala> new MyApp2
Map(db -> my_app_db2, foo -> bar, log -> false)
res1: MyApp2 = MyApp2@59d91aca

scala> new ConfApp with LogDisabled with LogEnabled {
     |   val dbName = "test1"
     |   println(conf)
     | }
Map(db -> test1, foo -> bar, log -> true)
res2: ConfApp with LogDisabled with LogEnabled = $anon$1@16dfdeda

scala> new ConfApp with LogEnabled with LogDisabled  {
     |   val dbName = "test2"
     |   println(conf)
     | }
Map(db -> test2, foo -> bar, log -> false)
res3: ConfApp with LogEnabled with LogDisabled = $anon$1@420c2f4a

如果你需要一个val conf而不是def conf,你可以这样做:
scala> class MyApp extends ConfApp {
     |   val dbName = "my_app_db"
     |   override val conf = super.conf
     |   println(conf)
     | }
defined class MyApp

scala> new MyApp
Map(db -> my_app_db, foo -> bar)
res4: MyApp = MyApp@17ebbd2a

1
如果我是你,我不会在这里使用vals,因为初始化顺序存在很多问题。你可以在特质中使用自引用来指定它们必须与ConfApp一起混合使用。话虽如此,我会像这样做:
trait LogEnabled { self: ConfApp =>
  self.conf("log") = true
}

trait LogDisabled { self: ConfApp =>
  self.conf("log") = false
}

trait ConfApp {
  def dbName: String
  lazy val conf: scala.collection.mutable.Map[String, Any] = scala.collection.mutable.Map("db" -> dbName, "foo" -> "bar")
}

class MyApp extends ConfApp {
  override def dbName = "my_app_db"
  println(conf)
}

class MyApp2 extends ConfApp with LogDisabled {
  override def dbName = "my_app_db"
  println(conf)
}

看起来运行良好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接