How to control the visibility of Scala constructor fields

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.”

Back to top

Problem

You want to control the visibility of fields that are used as constructor parameters in a Scala class.

Back to top

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 or val modifier, Scala gets conservative, and doesn’t generate a getter or setter method for the field.
  • Additionally, var and val fields can be modified with the private keyword, which prevents getters and setters from being generated.

See the examples that follow for more details.

Back to top

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.

Back to top

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.

Back to top

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
                ^
Back to top

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.

Back to top

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.

Back to top

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.

Back to top

The Scala Cookbook

This tutorial is sponsored by the Scala Cookbook, which I wrote for O’Reilly:

You can find the Scala Cookbook at these locations:

Back to top

Add new comment

The content of this field is kept private and will not be shown publicly.

Anonymous format

  • Allowed HTML tags: <em> <strong> <cite> <code> <ul type> <ol start type> <li> <pre>
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.