我浏览了有效的scala幻灯片,并在幻灯片10中提到了永远不要val
在trait
抽象成员中使用def
而是使用它。幻灯片没有详细说明为什么val
在a中使用abstracttrait
是反模式。如果有人可以解释在特征方法中使用val vs def的最佳实践,我将不胜感激
我浏览了有效的scala幻灯片,并在幻灯片10中提到了永远不要val
在trait
抽象成员中使用def
而是使用它。幻灯片没有详细说明为什么val
在a中使用abstracttrait
是反模式。如果有人可以解释在特征方法中使用val vs def的最佳实践,我将不胜感激
Answers:
Adef
可以通过a def
,a val
,alazy val
或an来实现object
。因此,这是定义成员的最抽象形式。由于特征通常是抽象接口,所以说您想要一个val
就是说实现应如何做。如果要求a val
,则实现类不能使用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
}
}
如果你有
trait Foo { val bar: Int }
您将无法定义F1
或F3
。
好的,让您困惑并回答@ om-nom-nom -使用abstract val
s可能会导致初始化问题:
trait Foo {
val bar: Int
val schoko = bar + bar
}
object Fail extends Foo {
val bar = 33
}
Fail.schoko // zero!!
在我个人看来,这是一个丑陋的问题,应该通过在编译器中对其进行修复来在以后的Scala版本中解决,但是是的,当前这也是为什么不应该使用abstract val
s的原因。
编辑(2016年1月):允许您使用实现覆盖抽象val
声明lazy val
,这样也可以防止初始化失败。
val
一个lazy val
。你的说法,你将无法建立F3
,如果bar
是val
不正确的。这就是说,在性状抽象成员应始终def
的
val schoko = bar + bar
有lazy val schoko = bar + bar
。这是对初始化顺序进行某种控制的一种方法。另外,在派生类中使用lazy val
代替def
可以避免重新计算。
val bar: Int
到def bar: Int
Fail.schoko
仍然是零。
我不建议val
在traits中使用,因为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
}
此代码产生错误:
StableIdentifier.scala:14: error: stable identifier required, but Access2.this.holder found.
def access : holder.Inner =
如果花一分钟的时间来思考,您会理解编译器有理由抱怨。在这种Access2.access
情况下,它不能以任何方式导出返回类型。def holder
意味着可以广泛实施。它可以为每个呼叫返回不同的持有人,并且持有人将合并不同的Inner
类型。但是Java虚拟机希望返回相同的类型。
始终使用def似乎有点尴尬,因为类似这样的方法将不起作用:
trait Entity { def id:Int}
object Table {
def create(e:Entity) = {e.id = 1 }
}
您将收到以下错误:
error: value id_= is not a member of Entity
var
。关键是,如果它们是字段,则应这样指定。我只是认为拥有def
短视的一切。
var
我们让您破坏封装。但是,使用def
(或val
)比使用全局变量更可取。我认为您正在寻找的是类似的东西,case class ConcreteEntity(override val id: Int) extends Entity
因此您可以从中创建它,def create(e: Entity) = ConcreteEntity(1)
这比破坏封装并允许任何类更改Entity更安全。