2016-02-12 63 views
2

标题可能不太清楚。这是我的问题。斯卡拉 - 特征成员初始化:使用特征修改类成员

比方说,我有一个特征,它定义了一系列配置参数的应用程序。这些参数包含在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

嗯,处理初始化的美丽混合性状时的问题。 –

回答

2

你仍然可以使用一个不变的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 = [email protected] 

scala> new MyApp2 
Map(db -> my_app_db2, foo -> bar, log -> false) 
res1: MyApp2 = [email protected] 

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 = [email protected] 

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 = [email protected] 

如果你需要有一个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 = [email protected] 
2

如果我是你,我不会使用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) 
} 

这似乎工作正常。