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 vals会导致初始化问题:
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版本中通过修复它在编译器中消失,但是,是的,目前这也是为什么不应该使用抽象vals的原因.
编辑(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虚拟机需要返回相同的类型.