我需要一个智能的组件组合机制,它允许混合特性在组合组件之后初始化。以下内容引发NullPointerException
:
class Component {
def addListener(pf: PartialFunction[Any, Unit]) {}
}
trait DynamicComponent {
protected def component: Component
component.addListener {
case x =>
}
}
class Foo extends DynamicComponent {
protected val component = new Component
}
new Foo // -> NullPointerException
对我来说,以下是 not options :
protected lazy val component
;这将产生一个 avalange 的数十个需要变得懒惰的val,我不想要。addListener
放入方法,例如initDynamic()
;因为我会混合很多特质,我不想记得打电话给六个initFoo()
方法。DelayedInit
。这不适用于特征,至少根据scaladocs。我可以接受一次init()
通话,但仅限于以下条件:
init()
语句是一个编译错误。答案 0 :(得分:10)
您可以使用早期定义来延迟特征的初始化。 (参见scala language specification)的第5.1.6节
class Foo extends {
protected val component = new Component
} with DynamicComponent
答案 1 :(得分:2)
它甚至比您的解决方案更笨拙,但您始终需要创建必须使用init()
方法设置的val。你可以选择不做最后一次并在运行时出错,但至少你不会完全忘记它:
class Component {
def addListener(pf: PartialFunction[Any, Unit]) {
println("Added")
}
}
trait Dyn {
protected def component: Component
protected val initialized: Init
class Init private () {}
private object Init { def apply() = new Init() }
def init() = { component.addListener{ case x => }; Init() }
}
class Foo extends Dyn {
protected val component = new Component
protected val initialized = init()
}
没有作弊!:
> class Bar extends Dyn { protected val component = new Component }
<console>:12: error: class Bar needs to be abstract, since value
initialized in trait Dyn of type Bar.this.Init is not defined
class Bar extends Dyn { protected val component = new Component }
这样做的好处是,如果您在合作初始化所有内容之前需要多个内容,或者Component
类是final
,那么您就无法混合任何其他内容。
答案 2 :(得分:1)
一个想法可能是使用这里描述的技巧: Cake pattern: how to get all objects of type UserService provided by components
应该初始化的所有组件都可以在某些Seq[InitializableComponent]
中注册。然后,您可以使用foreach初始化所有已注册的组件。
在Seq中没有任何组件会被遗忘,因为它们是自动注册的,但你仍然可以忘记给foreach打电话......
答案 3 :(得分:0)
这是一个想法(我很高兴看到其他建议):
class Component {
def addListener(pf: PartialFunction[Any, Unit]) {
println("Added")
}
}
trait DynamicComponentHost {
protected def component: Component with DynamicPeer
protected trait DynamicPeer {
_: Component =>
addListener {
case x =>
}
}
}
class Foo extends DynamicComponentHost {
protected val component = new Component with DynamicPeer
}
new Foo
所以基本上我强迫组件混合在一个只能由混合特征提供的类型中。合理?我的眼睛看起来有点太复杂了。