When declaring abstract fields in an abstract class or a trait, it’s good practice to declare them as paren-less methods.
Prefer:
abstract class Foo {
def bar: Int
}
Over:
abstract class Foo {
val bar: Int
}
Scala allows abstract paren-less methods to be implemented as val
s, but not the other way around.
Given the following:
abstract class AsDef {
def bar: Int
}
abstract class AsVal {
val bar: Int
}
Implementing AsDef.bar
as a val
compiles:
new AsDef {
override val bar = 0
}
But implementing AsVal.bar
as a def
does not:
new AsVal {
override def bar = 0
}
// error: overriding value bar in class AsVal of type Int;
// method bar needs to be a stable, immutable value
// override def bar = 0
// ^^^^^^^^^^^^^^^^^^^^
Declaring abstract fields as val
s closes some possibilities, while declaring them as paren-less methods has no ill effect.
There’s at least one known scenario where declaring an abstract field as a val
is important: path-dependent types, where its important that the field has a concrete type.
Here, for example:
trait Foo {
val bar: String
}
val foo: Foo = new Foo {
override val bar = "baz"
}
It’s possible to to refer explicitly to the type of foo.bar
:
val fooBar: foo.bar.type = foo.bar
Had we defined bar
as a def
:
trait Foo {
def bar: String
}
val foo: Foo = new Foo {
override val bar = "baz"
}
Then we’d get a compilation error attempting to get the type of foo.bar
:
val fooBar: foo.bar.type = foo.bar
// error: stable identifier required, but foo.bar.type found.
// val fooBar: foo.bar.type = foo.bar
// ^^^^^^^^^^^^