You want to extend a base class, and need to work with the constructor parameters declared in the base class, as well as new parameters in the subclass.
Declare your base class as usual with val
or var
constructor parameters. When defining a subclass constructor, leave the
val
or var
declaration off of the fields that are
common to both classes. Then define new constructor parameters in the
subclass as val
or var
fields, as usual.
For example, first define a Person
base class:
class
Person
(
var
name
:
String
,
var
address
:
Address
)
{
override
def
toString
=
if
(
address
==
null
)
name
else
s
"$name @ $address"
}
Next define Employee
as a
subclass of Person
, so that it takes
the constructor parameters name
,
address
, and age
. The name
and address
parameters are common to the parent
Person
class, so leave the var
declaration off of those fields, but
age
is new, so declare it as a
var
:
class
Employee
(
name
:
String
,
address
:
Address
,
var
age
:
Int
)
extends
Person
(
name
,
address
)
{
// rest of the class
}
With this Employee
class and an
Address
case class:
case
class
Address
(
city
:
String
,
state
:
String
)
you can create a new Employee
as follows:
val
teresa
=
new
Employee
(
"Teresa"
,
Address
(
"Louisville"
,
"KY"
),
25
)
By placing all that code in the REPL, you can see that all of the fields work as expected:
scala>teresa.name
res0: String = Teresa scala>teresa.address
res1: Address = Address(Louisville,KY) scala>teresa.age
res2: Int = 25
To understand how constructor parameters in a subclass work, it
helps to understand how the Scala compiler translates your code. Because
the following Person
class defines
its constructor parameters as var
fields:
class
Person
(
var
name
:
String
,
var
address
:
Address
)
{
override
def
toString
=
if
(
address
==
null
)
name
else
s
"$name @ $address"
}
the Scala compiler generates both accessor and mutator methods for
the class. You can demonstrate this by compiling and then disassembling
the Person
class.
First, put this code in a file named Person.scala:
case
class
Address
(
city
:
String
,
state
:
String
)
class
Person
(
var
name
:
String
,
var
address
:
Address
)
{
override
def
toString
=
if
(
address
==
null
)
name
else
s
"$name @ $address"
}
Then compile the code with scalac
, and disassemble the Person.class file with javap
:
$ javap Person
Compiled from "Person.scala"
public class Person extends java.lang.Object implements scala.ScalaObject{
public java.lang.String name();
public void name_$eq(java.lang.String);
public Address address();
public void address_$eq(Address);
public java.lang.String toString();
public Person(java.lang.String, Address);
}
As shown, the Person
class
contains the name
, name_$eq
, address
, and address_$eq
methods, which are the accessor
and mutator methods for the name
and
address
fields. (See Recipe 6.8 for an explanation
of how those mutator methods work.)
This raises the question, if you define an Employee
class that extends Person
, how should you handle the name
and address
fields in the Employee
constructor? Assuming Employee
adds no new parameters, there are at
least two main choices:
// Option 1: define name and address as 'var'
class
Employee
(
var
name
:
String
,
var
address
:
Address
)
extends
Person
(
name
,
address
)
{
...
}
// Option 2: define name and address without var or val
class
Employee
(
name
:
String
,
address
:
Address
)
extends
Person
(
name
,
address
)
{
...
}
Because Scala has already generated the getter and setter methods
for the name
and address
fields in the Person
class, the solution is to declare the
Employee
constructor without var
declarations:
// this is correct
class
Employee
(
name
:
String
,
address
:
Address
)
extends
Person
(
name
,
address
)
{
...
}
Because you don’t declare the parameters in Employee
as var
, Scala won’t attempt to generate methods
for those fields. You can demonstrate this by adding the Employee
class definition to the code in
Person.scala:
case
class
Address
(
city
:
String
,
state
:
String
)
class
Person
(
var
name
:
String
,
var
address
:
Address
)
{
override
def
toString
=
if
(
address
==
null
)
name
else
s
"$name @ $address"
}
class
Employee
(
name
:
String
,
address
:
Address
)
extends
Person
(
name
,
address
)
{
// code here ...
}
Compiling the code with scalac
and then disassembling the Employee.class file with javap
, you see the following, expected
result:
$ javap Employee
Compiled from "Person.scala"
public class Employee extends Person implements scala.ScalaObject{
public Employee(java.lang.String, Address);
}
The Employee
class extends
Person
, and Scala did not generate
any methods for the name
and address
fields. Therefore, the Employee
class inherits that behavior from
Person
.
While this example shows how Scala works with var
fields, you can follow the same line of
reasoning with val
fields as
well.
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.