How to override default accessors and mutators in Scala classes

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 4.6, “How to override default accessors and mutators in Scala classes.”

Problem

You want to override the getter or setter methods that Scala generates for you.

Solution

This is a bit of a trick problem, because you can’t override the getter and setter methods Scala generates for you, at least not if you want to stick with the Scala naming conventions. For instance, if you have a class named Person with a constructor parameter named name, and attempt to create getter and setter methods according to the Scala conventions, your code won’t compile:

// error: this won't work
class Person(private var name: String) {
    // this line essentially creates a circular reference
    def name = name
    def name_=(aName: String) { name = aName }
}

Attempting to compile this code generates three errors:

Person.scala:3: error: overloaded method name needs result type
  def name = name
             ^

Person.scala:4: error: ambiguous reference to overloaded definition,
both method name_= in class Person of type (aName: String)Unit
and  method name_= in class Person of type (x$1: String)Unit
match argument types (String)
  def name_=(aName: String) { name = aName }
                              ^
Person.scala:4: error: method name_= is defined twice
  def name_=(aName: String) { name = aName }
      ^
three errors found

I’ll examine these problems more in the Discussion, but the short answer is that both the constructor parameter and the getter method are named name, and Scala won’t allow that.

To solve this problem, change the name of the field you use in the class constructor so it won’t collide with the name of the getter method you want to use. A common approach is to add a leading underscore to the parameter name, so if you want to manually create a getter method called name, use the parameter name _name in the constructor, then declare your getter and setter methods according to the Scala conventions:

class Person(private var _name: String) {
    def name = _name                             // accessor
    def name_=(aName: String) { _name = aName }  // mutator
}

Notice the constructor parameter is declared private and var. The private keyword keeps Scala from exposing that field to other classes, and the var lets the value of the field be changed.

Creating a getter method named name and a setter method named name_= conforms to the Scala convention and lets a consumer of your class write code like this:

val p = new Person("Jonathan")
p.name = "Jony"    // setter
println(p.name)    // getter

If you don’t want to follow this Scala naming convention for getters and setters, you can use any other approach you want. For instance, you can name your methods getName and setName, following the JavaBean style. (However, if JavaBeans are what you really want, you may be better off using the @BeanProperty annotation, as described in Recipe 17.6, “When Java Code Requires JavaBeans”.)

Discussion

When you define a constructor parameter to be a var field, Scala makes the field private to the class and automatically generates getter and setter methods that other classes can use to access the field. For instance, given a simple class like this:

class Stock (var symbol: String)

after the class is compiled with scalac, you’ll see this signature when you disassemble it with javap:

$ javap Stock
public class Stock extends java.lang.Object{
    public java.lang.String symbol();
    public void symbol_$eq(java.lang.String);
    public Stock(java.lang.String);
}

You can see that the Scala compiler generated two methods: a getter named symbol and a setter named symbol_$eq. This second method is the same as a method you’d name symbol_=, but Scala needs to translate the = symbol to $eq to work with the JVM.

That second method name is a little unusual, but it follows a Scala convention, and when it’s mixed with some syntactic sugar, it lets you set the symbol field on a Stock instance like this:

stock.symbol = "GOOG"

The way this works is that behind the scenes, Scala converts that line of code into this line of code:

stock.symbol_$eq("GOOG")

You generally never have to think about this, unless you want to override the mutator method.

Summary

As shown in the Solution, the recipe for overriding default getter and setter methods is:

  1. Create a private var constructor parameter with a name you want to reference from within your class. In the example in the Solution, the field is named _name.
  2. Define getter and setter names that you want other classes to use. In the Solution the getter name is name, and the setter name is name_= (which, combined with Scala’s syntactic sugar, lets users write p.name = "Jony").
  3. Modify the body of the getter and setter methods as desired.

It’s important to remember the private setting on your field. If you forget to control the access with private (or private[this]), you’ll end up with getter/setter methods for the field you meant to hide. For example, in the following code, I intentionally left the private modifier off of the _symbol constructor parameter:

// intentionally left the 'private' modifier off _symbol
class Stock (var _symbol: String) {

    // getter
    def symbol = _symbol

    // setter
    def symbol_= (s: String) {
        this.symbol = s
        println(s"symbol was updated, new value is $symbol")
    }
}

Compiling and disassembling this code shows the following class signature, including two methods I “accidentally” made visible:

public class Stock extends java.lang.Object{
    public java.lang.String _symbol();           // error
    public void _symbol_$eq(java.lang.String);   // error
    public java.lang.String symbol();
    public void symbol_$eq(java.lang.String);
    public Stock(java.lang.String);
}

Correctly adding private to the _symbol field results in the correct signature in the disassembled code:

public class Stock extends java.lang.Object{
    public java.lang.String symbol();          // println(stock.symbol)
    public void symbol_$eq(java.lang.String);  // stock.symbol = "AAPL"
    public Stock(java.lang.String);
}

Note that while these examples used fields in a class constructor, the same principles hold true for fields defined inside a class.

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:

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.