Controlling the Visibility of 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 valvar, 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.

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 orvar 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, 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, for more information about how case classes work.

你可能感兴趣的:(Constructor)