Man*_*raf 84 inheritance scala traits
我正想通过有效的斯卡拉幻灯片,并提到在幻灯片10至从来不使用val
的trait
抽象成员和使用def
来代替.幻灯片没有详细提及为什么val
在a中使用抽象trait
是一种反模式.如果有人可以解释在抽象方法的特性中使用val vs def的最佳实践,我将不胜感激
0__*_*0__ 123
A def
可以通过a def
,a val
,a lazy val
或a来实现object
.所以这是定义成员最抽象的形式.由于性状通常是抽象接口,说你想有一个val
是说如何实施应该做的.如果你要求a val
,实现类不能使用a def
.
val
只有在需要稳定标识符时才需要A ,例如,对于路径相关类型.这是你通常不需要的东西.
相比:
trait Foo { def bar: Int }
object F1 extends Foo { def bar = util.Random.nextInt(33) } // ok
class F2(val bar: Int) extends Foo // ok
object F3 extends Foo {
lazy val bar = { // ok
Thread.sleep(5000) // really heavy number crunching
42
}
}
Run Code Online (Sandbox Code Playgroud)
如果你有
trait Foo { val bar: Int }
Run Code Online (Sandbox Code Playgroud)
你将无法定义F1
或F3
.
好的,混淆你并回答@ om-nom-nom-using abstract val
s会导致初始化问题:
trait Foo {
val bar: Int
val schoko = bar + bar
}
object Fail extends Foo {
val bar = 33
}
Fail.schoko // zero!!
Run Code Online (Sandbox Code Playgroud)
这是一个丑陋的问题,在我个人看来,它应该在未来的Scala版本中通过修复它在编译器中消失,但是,是的,目前这也是为什么不应该使用抽象val
s的原因.
编辑(2016年1月):您可以使用实现覆盖抽象val
声明lazy val
,这样也可以防止初始化失败.
我不喜欢val
在traits中使用,因为val声明具有不清楚且不直观的初始化顺序.你可以在已经工作的层次结构中添加一个特征,它会破坏之前有用的所有东西,请参阅我的主题:为什么在非final类中使用普通的val
你应该记住使用这个val声明的所有事情,这最终会导致你的错误.
但有时候你无法避免使用val
.正如@ 0__有时提到的那样,你需要一个稳定的标识符,def
而不是一个.
我想举一个例子来说明他在说什么:
trait Holder {
type Inner
val init : Inner
}
class Access(val holder : Holder) {
val access : holder.Inner =
holder.init
}
trait Access2 {
def holder : Holder
def access : holder.Inner =
holder.init
}
Run Code Online (Sandbox Code Playgroud)
此代码产生错误:
StableIdentifier.scala:14: error: stable identifier required, but Access2.this.holder found.
def access : holder.Inner =
Run Code Online (Sandbox Code Playgroud)
如果你花一点时间认为你会理解编译器有理由抱怨.在这种Access2.access
情况下,它无法通过任何方式获得返回类型.def holder
意味着它可以广泛实施.它可以为每次通话返回不同的持有者,并且持有者将包含不同Inner
类型.但Java虚拟机需要返回相同的类型.