2016-02-12 40 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) 
} 

這似乎工作正常。