In Scala, a val
can override a def
, but a def
cannot override a val
.
So, is there an advantage to declaring a trait e.g. like this:
trait Resource {
val id: String
}
rather than this?
trait Resource {
def id: String
}
The follow-up question is: how does the compiler treat calling val
s and def
s differently in practice and what kind of optimizations does it actually do with val
s? The compiler insists on the fact that val
s are stable — what does in mean in practice for the compiler? Suppose the subclass is actually implementing id
with a val
. Is there a penalty for having it specified as a def
in the trait?
If my code itself does not require stability of the id
member, can it be considered good practice to always use def
s in these cases and to switch to val
s only when a performance bottleneck has been identified here — however unlikely this may be?
Short answer:
As far as I can tell, the values are always accessed through the accessor method. Using
def
defines a simple method, which returns the value. Usingval
defines a private [*] final field, with an accessor method. So in terms of access, there is very little difference between the two. The difference is conceptual,def
gets reevaluated each time, andval
is only evaluated once. This can obviously have an impact on performance.[*] Java private
Long answer:
Let's take the following example:
The
ResourceDef
&ResourceVal
produce the same code, ignoring initializers:For the subsidiary classes produced (which contain the implementation of the methods), the
ResourceDef
produces is as you would expect, noting that the method is static:and for the val, we simply call the initialiser in the containing class
When we start extending:
Where we override, we get a method in the class which just does what you expect. The def is simple method:
and the val defines a private field and accessor method:
Note that even
foobar()
doesn't use the fieldid
, but uses the accessor method.And finally, if we don't override, then we get a method which calls the static method in the trait auxiliary class:
I've cut out the constructors in these examples.
So, the accessor method is always used. I assume this is to avoid complications when extending multiple traits which could implement the same methods. It gets complicated really quickly.
Even longer answer:
Josh Suereth did a very interesting talk on Binary Resilience at Scala Days 2012, which covers the background to this question. The abstract for this is:
A
val
expression is evaluated once on variable declaration, it is strict and immutable.A
def
is re-evaluated each time you call itThe difference is mainly that you can implement/override a def with a val but not the other way around. Moreover val are evaluated only once and def are evaluated every time they are used, using def in the abstract definition will give the code who mixes the trait more freedom about how to handle and/or optimize the implementation. So my point is use defs whenever there isn't a clear good reason to force a val.
def
is evaluated by name andval
by value. This means more or less thatval
must always return an actual value, whiledef
is more like a promess that you can get a value when evaluating it. For example, if you have a functionthat logs an event only if the log level is set to trace and you want to log an objects
toString
. If you have overridentoString
with a value, then you need to pass that value to thetrace
function. IftoString
however is adef
, it will only be evaluated once it's sure that the log level is trace, which could save you some overhead.def
gives you more flexibility, whileval
is potentially fasterCompilerwise,
traits
are compiled to java interfaces so when defining a member on atrait
, it makes no difference if its avar
ordef
. The difference in performance would depend on how you choose to implement it.