You want to create a primary constructor for a class, and you quickly find that the approach is different than Java.
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
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
;
Note
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.
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.
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.
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.
Get Scala Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.