You want to control the visibility of fields that are used as constructor parameters in a Scala class.
As shown in the following examples, the visibility of constructor
fields in a Scala class is controlled by whether the fields are declared
as val
, var
, without either val
or var
,
and whether private
is also added to
the fields.
Here’s the short version of the solution:
If a field is declared as a
var
, Scala generates both getter and setter methods for that field.If the field is a
val
, Scala generates only a getter method for it.If a field doesn’t have a
var
orval
modifier, Scala gets conservative, and doesn’t generate a getter or setter method for the field.Additionally,
var
andval
fields can be modified with theprivate
keyword, which prevents getters and setters from being generated.
See the examples that follow for more details.
If a constructor parameter is declared as a var
, the value of the field
can be changed, so Scala generates both getter
and setter methods for that field. In the following examples, the
constructor parameter name
is
declared as a var
, so the field can
be accessed and mutated:
scala>class Person(var name: String)
defined class Person scala>val p = new Person("Alvin Alexander")
p: Person = Person@369e58be // getter scala>p.name
res0: String = Alvin Alexander // setter scala>p.name = "Fred Flintstone"
p.name: String = Fred Flintstone scala>p.name
res1: String = Fred Flintstone
As shown, Scala does not follow the JavaBean naming convention when generating accessor and mutator methods.
If a constructor field is defined as a val
, the value of the field
can’t be changed once it’s been set; it’s
immutable (like final
in Java).
Therefore it makes sense that it should have an accessor method, and
should not have a mutator method:
scala>class Person(val name: String)
defined class Person scala>val p = new Person("Alvin Alexander")
p: Person = Person@3f9f332b scala>p.name
res0: String = Alvin Alexander scala>p.name = "Fred Flintstone"
<console>:11: error: reassignment to val p.name = "Fred Flintstone" ^
The last example fails because a mutator method is not generated
for a val
field.
When neither val
nor var
are specified on constructor parameters,
the visibility of the field becomes very restricted, and Scala doesn’t
generate accessor or mutator methods:
scala>class Person(name: String)
defined class Person scala>val p = new Person("Alvin Alexander")
p: Person = Person@144b6a6c scala>p.name
<console>:12: error: value name is not a member of Person p.name ^
In addition to these three basic configurations, you can add the
private
keyword to a val
or var
field. This keyword prevents getter and
setter methods from being generated, so the field can only be accessed
from within members of the class:
scala>class Person(private var name: String) { def getName {println(name)} }
defined class Person scala>val p = new Person("Alvin Alexander")
p: Person = Person@3cb7cee4 scala>p.name
<console>:10: error: variable name in class Person cannot be accessed in Person p.name ^ scala>p.getName
Alvin Alexander
Attempting to access p.name
fails because a getter method is not generated for the name
field, so callers can’t access it
directly, but p.getName
works
because it can access the name
field.
If this is a little confusing, it helps to think about the choices
the compiler has when generating code for you. When a field is defined
as a val
, by definition its value
can’t be changed, so it makes sense to generate a getter, but no setter.
By definition, the value of a var
field can be changed, so generating both a getter
and setter make sense for it.
The private
setting on a
constructor parameter gives you additional flexibility. When it’s added
to a val
or var
field, the getter and setter methods are
generated as before, but they’re marked private
. (I rarely use this feature, but it’s
there if you need it.)
The accessors and mutators that are generated for you based on these settings are summarized in Table 4-1.
Table 4-1. The effect of constructor parameter settings
Visibility | Accessor? | Mutator? |
---|---|---|
| Yes | Yes |
| Yes | No |
Default visibility (no
| No | No |
Adding the | No | No |
You can also manually add your own accessor and mutator methods. See Recipe 4.6, for more information.
Parameters in the constructor of a case
class differ from these rules in one way. Case class
constructor parameters are val
by
default. So if you define a case class field without adding val
or var
, like this:
case
class
Person
(
name
:
String
)
you can still access the field, just as if it were defined as a
val
:
scala>val p = Person("Dale Cooper")
p: Person = Person(Dale Cooper) scala>p.name
res0: String = Dale Cooper
Although this is slightly different than a “regular” class, it’s a nice convenience and has to do with the way case classes are intended to be used in functional programming, i.e., as immutable records. See Recipe 4.14, for more information about how case classes work.
Get Scala Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.