This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 4.2, “How to control the visibility of Scala class constructor fields.”
Problem
You want to control the visibility of fields that are used as constructor parameters in a Scala class.
Solution
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.
var fields
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.
val fields
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.
Fields without val
or var
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 ^
Adding private to val
or var
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.
Discussion
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? |
---|---|---|
var |
Yes | Yes |
val |
Yes | No |
Default visibility (no var or val ) |
No | No |
Adding the private keyword to var or val |
No | No |
You can also manually add your own accessor and mutator methods. See Recipe 4.6, “Overriding Default Accessors and Mutators”, for more information.
Case classes
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, “Generating Boilerplate Code with Case Classes”, for more information about how case classes work.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |