问题
Probably the Title is not so clear. This is my problem.
Let's say I have a trait that defines an application with a series of configuration parameters. These parameters are contained in a Map
, some of them have default values.
trait ConfApp {
val dbName: String
lazy val conf: scala.collection.mutable.Map[String, Any] = scala.collection.mutable.Map("db" -> dbName, "foo" -> "bar")
}
So I can create a custom application as follows:
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)
I would like to create other traits that set some other configuration parameters. In other words I would like to be able to do something like:
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)
So far I've managed to do the following:
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)
but as you can see in M2
the db
parameter is null
. I can't understand what I'm doing wrong.
Sincerely, I don't like at all this approach with mutable Map, but I've not managed to do something better.
回答1:
You can still use an immutable Map
this way:
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
If you need to have a val conf
instead of def conf
you could do this:
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
回答2:
If I were you I wouldn't be using vals for this as there is a lot of issues with the order of initialization. For one you could also use self references in your traits to specify that they have to be mixed in together with ConfApp
. That said I'd do something like this:
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)
}
which seems to be working fine.
来源:https://stackoverflow.com/questions/35359022/scala-trait-member-initialization-use-traits-to-modify-class-member