Scala Setter Reassignment To Val Scala

In object oriented programming, getters and setters are something that most would agree have some real world benefits. Unfortunately, they can sometimes be annoying to write. They usually do not consist of a lot of code, but when you have the write the same thing over and over and over it gets old really fast. In my experience, most getters and setters are very much alike so it stands to reason that there must be a “better” way to accomplish the same result.

In some languages there is support of some kind of “missing_method” syntax that allows the language to dynamically create getters and setters just by looking at the name of the method that doesn’t exist in the object. For example, in ColdFusion, there exists an “onMissingMethod” method that, when included, automatically executes when a method does not exist. This onMissingMethod gets passed the method name that was trying to be accessed and any arguments passed in. Then, by inspecting the method name (lets say, setAge) the method type and property can be determined. From “setAge” it can be determined that the method is a setter and that it should set the property “age”.

Recently though I’ve done a lot of reading about and playing with Scala and I’ve found that it does things quite a bit different than ColdFusion, Java, and most other languages I’ve seen.

As a first example, lets see an example of a basic class definition in Scala.

At first glance you might think that this class only allows you to instantiate a person object but not anything more. But actually, once instantiated, you can set and get the properties by doing the following:

To set the properties to different values you would do the following:

Pretty simple so far. You might be thinking to yourself the same thing I thought when I saw an example like this: these aren’t getters and setters! And you are right, that is not what this is. This is simply retrieving the public properties directly from the object. In most languages this would be a very bad idea. What happens when you need to validate the age is in a certain range before it is set? What happens if you want to format the name in a particular way before returning it? In Java or other languages you would be in trouble right about now and probably kicking yourself for not taking the time to write getters and setters. If you wanted to switch from directly accessing the public properties to getters and setters you would either have to find some strange work-around or implement the getters and setters and change any code that access those properties directly to use the getters and setters instead.

In Scala though, it is no big deal to directly access public properties. You could easily change to using getters and setters by making a couple simple changes to your class.

What this code is doing is pretty simple. First, the variable “age” is renamed to “_age” and made private with the private keyword. Next, the getter is added by the line:

This code simply defines a method called “age” and returns the “_age” variable. Scala doesn’t require the return keyword but it would just have easily be written as:

In addition to not requiring the return keyword, Scala doesn’t mandate the use of curly braces around a method body if the body consists of a single expression.

Next, a setter had to be added to set the new private variable.

This line is a bit more tricky but I’ll explain. First, the method name is “age_=“. The underscore is a special character in Scala and in this case, allows for a space in the method name which essentially makes the name “age =”. The parentheses and contents dictate the value and type that needs to be passed in. The “:Unit” code is equivalent to returning void. The remaining code is setting the “_age” variable to “value”. These things allow the method to be used in the same way as directly accessing the public property.

But wait! Where are the parentheses in this supposed method call?! In Scala, parentheses are usually optional. The previous line could just as easily been written as

As you can see, Scala has some unconventional ideas that provide a solid, well thought out, and an abbreviated syntax to access object properties.

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 4.13, “How to define properties in an abstract base class or trait.”

Back to top

Problem

Using Scala, you want to define abstract or concrete properties in an abstract base class (or trait) that can be referenced in all child classes.

Back to top

Solution

You can declare both and fields in an abstract Scala class (or trait), and those fields can be abstract or have concrete implementations. All of these variations are shown in this recipe.

Back to top

Abstract val and var fields

The following example demonstrates an trait with abstract and fields, along with a simple concrete method named , and an override of the method:

abstract class Pet (name: String) { val greeting: String var age: Int def sayHello { println(greeting) } override def toString = s"I say $greeting, and I'm $age" }

The following and classes extend the class and provide values for the and fields. Notice that the fields are again specified as or :

class Dog (name: String) extends Pet (name) { val greeting = "Woof" var age = 2 } class Cat (name: String) extends Pet (name) { val greeting = "Meow" var age = 5 }

The functionality can be demonstrated with a simple driver object:

object AbstractFieldsDemo extends App { val dog = new Dog("Fido") val cat = new Cat("Morris") dog.sayHello cat.sayHello println(dog) println(cat) // verify that the age can be changed cat.age = 10 println(cat) }

The resulting output looks like this:

Woof Meow I say Woof, and I'm 2 I say Meow, and I'm 5 I say Meow, and I'm 10

Concrete field implementations are presented in the Discussion, because it helps to understand how the Scala compiler translates your code in the preceding examples.

Back to top

Discussion

As shown, you can declare abstract fields in an abstract class as either or , depending on your needs. The way abstract fields work in abstract classes (or traits) is interesting:

  • An abstract field results in getter and setter methods being generated for the field.
  • An abstract field results in a getter method being generated for the field.
  • When you define an abstract field in an abstract class or trait, the Scala compiler does not create a field in the resulting code; it only generates the methods that correspond to the or field.

In the example shown in the Solution, if you look at the code that’s created by using the option, or by decompiling the resulting Pet.class file, you won’t find or fields. For instance, if you decompile the class, the output shows only methods in the class, no fields:

import scala.*; import scala.runtime.BoxesRunTime; public abstract class Pet { public abstract String greeting(); public abstract int age(); public abstract void age_$eq(int i); public void sayHello() { Predef$.MODULE$.println(greeting()); } public String toString(){ // code omitted } public Pet(String name){} }

Because of this, when you provide concrete values for these fields in your concrete classes, you must again define your fields to be or . Because the fields don’t actually exist in the abstract base class (or trait), the keyword is not necessary.

As another result of this, you may see developers define a that takes no parameters in the abstract base class rather than defining a . They can then define a in the concrete class, if desired. This technique is demonstrated in the following code:

abstract class Pet (name: String) { def greeting: String } class Dog (name: String) extends Pet (name) { val greeting = "Woof" } object Test extends App { val dog = new Dog("Fido") println(dog.greeting) }

Given this background, it’s time to examine the use of concrete and fields in abstract classes.

Back to top

Concrete val fields in abstract classes

When defining a concrete field in an abstract class, you can provide an initial value, and then override that value in concrete subclasses:

abstract class Animal { val greeting = "Hello" // provide an initial value def sayHello { println(greeting) } def run } class Dog extends Animal { override val greeting = "Woof" // override the value def run { println("Dog is running") } }

In this example, the greeting variable is created in both classes. To demonstrate this, running the following code:

abstract class Animal { val greeting = { println("Animal"); "Hello" } } class Dog extends Animal { override val greeting = { println("Dog"); "Woof" } } object Test extends App { new Dog }

results in this output, showing that both values are created:

Animal Dog

To prove this, you can also decompile both the and classes, where you’ll find the declared like this:

private final String greeting = "Hello";

To prevent a concrete field in an abstract base class from being overridden in a subclass, declare the field as a :

abstract class Animal { final val greeting = "Hello" // made the field 'final' } class Dog extends Animal { val greeting = "Woof" // this line won't compile }Back to top

Concrete var fields in abstract classes

You can also give fields an initial value in your trait or abstract class, and then refer to them in your concrete subclasses, like this:

abstract class Animal { var greeting = "Hello" var age = 0 override def toString = s"I say $greeting, and I'm $age years old." } class Dog extends Animal { greeting = "Woof" age = 2 }

In this case, these fields are declared and assigned in the abstract base class, as shown in the decompiled code for the class:

private String greeting; private int age; public Animal(){ greeting = "Hello"; age = 0; } // more code ...

Because the fields are declared and initialized in the abstract base class, there’s no need to redeclare the fields as or in the concrete subclass.

You can verify this by looking at the code the Scala compiler generates for the class. When you compile the code with , and look at the last lines of output, you’ll see how the compiler has converted the class:

class Dog extends Animal { def <init>(): Dog = { Dog.super.<init>(); Dog.this.greeting_=("Woof"); Dog.this.age_=(2); () } }

Because the fields are concrete fields in the abstract base class, they just need to be reassigned in the concrete class.

Back to top

Don’t use null

As discussed in many recipes in this book, including Recipe 20.5, “Eliminate null Values from Your Code”, you shouldn’t use values in these situations. If you’re tempted to use a , instead initialize the fields using the Option/Some/None pattern. The following example demonstrates how to initialize and fields with this approach:

trait Animal { val greeting: Option[String] var age: Option[Int] = None override def toString = s"I say $greeting, and I'm $age years old." } class Dog extends Animal { val greeting = Some("Woof") age = Some(2) } object Test extends App { val d = new Dog println(d) }

Running this object yields the following output:

I say Some(Woof), and I'm Some(2) years old.Back to top

See Also

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

0 comments

Leave a Reply

Your email address will not be published. Required fields are marked *