I was going through the effective scala slides and it mentions on slide 10 to never use val
in a trait
for abstract members and use def
instead. The slide does not mention in detail why using abstract val
in a trait
is an anti-pattern. I would appreciate it if someone can explain best practice around using val vs def in a trait for abstract methods
That's all about the difference between var, val, and def in Scala. In short, the val and var are evaluated when defined, while def is evaluated on call. Also, val defines a constant, a fixed value that cannot be modified once declared and assigned while var defines a variable, which can be modified or reassigned.
def is the keyword you use to define a method, the method name is double , and the input parameter a has the type Int , which is Scala's integer data type. The body of the function is shown on the right side, and in this example it simply doubles the value of the input parameter a : def double(a: Int) = a * 2 -----
Traits are used to define object types by specifying the signature of the supported methods. Scala also allows traits to be partially implemented but traits may not have constructor parameters. A trait definition looks just like a class definition except that it uses the keyword trait.
In scala, trait is a collection of abstract and non-abstract methods. You can create trait that can have all abstract methods or some abstract and some non-abstract methods. A variable that is declared either by using val or var keyword in a trait get internally implemented in the class that implements the trait.
A def
can be implemented by either of a def
, a val
, a lazy val
or an object
. So it's the most abstract form of defining a member. Since traits are usually abstract interfaces, saying you want a val
is saying how the implementation should do. If you ask for a val
, an implementing class cannot use a def
.
A val
is needed only if you need a stable identifier, e.g. for a path-dependent type. That's something you usually don't need.
Compare:
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 } }
If you had
trait Foo { val bar: Int }
you wouldn't be able to define F1
or F3
.
Ok, and to confuse you and answer @om-nom-nom—using abstract val
s can cause initialisation problems:
trait Foo { val bar: Int val schoko = bar + bar } object Fail extends Foo { val bar = 33 } Fail.schoko // zero!!
This is an ugly problem which in my personal opinion should go away in future Scala versions by fixing it in the compiler, but yes, currently this is also a reason why one should not use abstract val
s.
Edit (Jan 2016): You are allowed to override an abstract val
declaration with a lazy val
implementation, so that would also prevent the initialisation failure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With