This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 4.1, “How to create a primary constructor in a Scala class.”
Problem
You want to create a primary constructor for a Scala class, and you quickly find that the approach is different than Java.
Solution
The primary constructor of a Scala class is a combination of:
- The constructor parameters
- Methods that are called in the body of the class
- Statements and expressions that are executed in the body of the class
Fields declared in the body of a Scala class are handled in a manner similar to Java; they are assigned when the class is first instantiated.
The following class demonstrates constructor parameters, class fields, and statements in the body of a class:
class Person(var firstName: String, var lastName: String) { println("the constructor begins") // some class fields private val HOME = System.getProperty("user.home") var age = 0 // some methods override def toString = s"$firstName $lastName is $age years old" def printHome { println(s"HOME = $HOME") } def printFullName { println(this) } // uses toString printHome printFullName println("still in the constructor") }
Because the methods in the body of the class are part of the constructor, when an instance of a Person
class is created, you’ll see the output from the println
statements at the beginning and end of the class declaration, along with the call to the printHome
and printFullName
methods near the bottom of the class:
scala> val p = new Person("Adam", "Meyer") the constructor begins HOME = /Users/Al Adam Meyer is 0 years old still in the constructor
Discussion
If you’re coming to Scala from Java, you’ll find that the process of declaring a primary constructor in Scala is quite different. In Java it’s fairly obvious when you’re in the main
constructor and when you’re not, but Scala blurs this distinction. However, once you understand the approach, it also makes your class declarations more concise than Java class declarations.
In the example shown, the two constructor arguments firstName
and lastName
are defined as var
fields, which means that they’re variable, or mutable; they can be changed after they’re initially set. Because the fields are mutable, Scala generates both accessor and mutator methods for them. As a result, given an instance p
of type Person
, you can change the values like this:
p.firstName = "Scott" p.lastName = "Jones"
and you can access them like this:
println(p.firstName) println(p.lastName)
Because the age
field is declared as a var, it’s also visible, and can be mutated and accessed:
p.age = 30 println(p.age)
The field HOME
is declared as a private val
, which is like making it private
and final
in a Java class. As a result, it can’t be accessed directly by other objects, and its value can’t be changed.
When you call a method in the body of the class — such as the call near the bottom of the class to the printFullName
method — that method call is also part of the constructor. You can verify this by compiling the code to a Person.class file with scalac
, and then decompiling it back into Java source code with a tool like the JAD decompiler. After doing so, this is what the Person
class constructor looks like:
public Person(String firstName, String lastName) { super(); this.firstName = firstName; this.lastName = lastName; Predef$.MODULE$.println("the constructor begins"); age = 0; printHome(); printFullName(); Predef$.MODULE$.println("still in the constructor"); }
This clearly shows the printHome
and printFullName
methods call in the Person
constructor, as well as the initial age
being set.
When the code is decompiled, the constructor parameters and class fields appear like this:
private String firstName; private String lastName; private final String HOME = System.getProperty("user.home"); private int age;
Anything defined within the body of the class other than method declarations is a part of the primary class constructor. Because auxiliary constructors must always call a previously defined constructor in the same class, auxiliary constructors will also execute the same code.
A comparison with Java
The following code shows the equivalent Java version of the Person
class:
// java public class Person { private String firstName; private String lastName; private final String HOME = System.getProperty("user.home"); private int age; public Person(String firstName, String lastName) { super(); this.firstName = firstName; this.lastName = lastName; System.out.println("the constructor begins"); age = 0; printHome(); printFullName(); System.out.println("still in the constructor"); } public String firstName() { return firstName; } public String lastName() { return lastName; } public int age() { return age; } public void firstName_$eq(String firstName) { this.firstName = firstName; } public void lastName_$eq(String lastName) { this.lastName = lastName; } public void age_$eq(int age) { this.age = age; } public String toString() { return firstName + " " + lastName + " is " + age + " years old"; } public void printHome() { System.out.println(HOME); } public void printFullName() { System.out.println(this); } }
As you can see, this is quite a bit lengthier than the equivalent Scala code. With constructors, I find that Java code is more verbose, but obvious; you don’t have to reason much about what the compiler is doing for you.
Those _$eq
methods
The names of the mutator methods that are generated may look a little unusual:
public void firstName_$eq(String firstName) { ... public void age_$eq(int age) { ...
These names are part of the Scala syntactic sugar for mutating var
fields, and not anything you normally have to think about. For instance, the following Person
class has a var
field named name
:
class Person { var name = "" override def toString = s"name = $name" }
Because name
is a var
field, Scala generates accessor and mutator methods for it. What you don’t normally see is that when the code is compiled, the mutator method is named name_$eq
. You don’t see that because with Scala’s syntactic sugar, you mutate the field like this:
p.name = "Ron Artest"
However, behind the scenes, Scala converts that line of code into this code:
p.name_$eq("Ron Artest")
To demonstrate this, you can run the following object that calls the mutator method in both ways (not something that’s normally done):
object Test extends App { val p = new Person // the 'normal' mutator approach p.name = "Ron Artest" println(p) // the 'hidden' mutator method p.name_$eq("Metta World Peace") println(p) }
When this code is run, it prints this output:
name = Ron Artest name = Metta World Peace
Again, there’s no reason to call the name_$eq
method in the real world, but when you get into overriding mutator methods, it’s helpful to understand how this translation process works.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
Summary
As shown with the equivalent Scala and Java classes, the Java code is verbose, but it’s also straightforward. The Scala code is more concise, but you have to look at the constructor parameters to understand whether getters and setters are being generated for you, and you have to know that any method that’s called in the body of the class is really being called from the primary constructor. This was a little confusing when I first started working with Scala, but it quickly became second nature.