What a constructor does
A constructor runs when an object is created, and sets up its initial state. Without one, you would create a Dog and then fill in its fields line by line:
val dog = Dog()
dog.name = "Rex"
dog.age = 4
That works, but it is easy to forget a line and leave an object half set up. A constructor with parameters fills in the fields at the moment the object is created.
The primary constructor
In Scala, a class's main constructor is part of the class header. The parameters go in parentheses right after the class name:
class Dog(var name: String, var age: Int)
This one line does a lot. It defines a constructor that takes a String and an Int, and because each parameter is marked with var, Scala also creates a field for it, readable and writable from outside. A class with no body needs no braces. Creating objects is then just a call with arguments:
val rex = Dog("Rex", 4)
val luna = Dog("Luna", 7)
println(rex.name + " is " + rex.age)
println(luna.name + " is " + luna.age)
This prints:
Rex is 4
Luna is 7
The fields are already set when Dog(...) returns. Because they are var fields, they can be changed afterwards: rex.age = 5 is valid.
The modifier in front of a parameter decides what you get
var name: Stringcreates a field that can be read and reassigned from outside.val name: Stringcreates a field that can be read but not reassigned. Writingrex.name = "y"is rejected withReassignment to val name.name: String, with neither, is only a parameter. The class body can use it, but it is not a field that outsiders can reach. Readingrex.namefrom outside fails withvalue name cannot be accessed as a member of Dog.
This is why the lesson's class uses var on both parameters: the code in mainFunction reads rex.name and rex.age from outside the class.
Calling the constructor
The constructor needs all of its arguments. After class Dog(var name: String, var age: Int), a call with one argument doesn't compile. The compiler reports missing argument for parameter age. A call with no arguments fails in the same way, which means the free no-argument constructor of a class without a parameter list is gone as soon as you declare parameters.
Parameters can have default values, which makes some of them optional:
class Cat(val name: String, val age: Int = 1)
val a = Cat("Tom") // age is 1
val b = Cat("Tom", 3) // age is 3
Code in the class body
Statements written directly in the class body belong to the primary constructor and run each time an object is created:
class Cat(val name: String) {
println(s"Creating $name")
}
Calling Cat("Tom") prints Creating Tom.
Auxiliary constructors
A class can have extra constructors, declared with def this. They have no return type, and their first statement must call another constructor of the same class:
class Dog(var name: String, var age: Int) {
def this(name: String) = this(name, 0)
}
Here Dog("Rex") calls the one-argument constructor, which hands over to the primary one with age 0. A class with no primary parameters can also write def this(name: String, age: Int) = { this(); this.name = name; this.age = age } and assign fields in the body. That works, but the primary constructor is shorter and is the usual choice.
The this keyword
When a parameter has the same name as a field, the bare name refers to the parameter, because the closest definition wins. this stands for the current object, so this.name is the field. With a var in the primary constructor, you don't need this assignment at all, since Scala creates the field and fills it in for you.
The exercise
Declare Dog with a primary constructor taking name and age as var parameters, then create the two dogs in mainFunction:
class Dog(var name: String, var age: Int)
class Solution {
def mainFunction(): Unit = {
val rex = Dog("Rex", 4)
val luna = Dog("Luna", 7)
println(rex.name + " is " + rex.age)
println(luna.name + " is " + luna.age)
}
}
The output is the two lines Rex is 4 and Luna is 7.
Key takeaways
- The primary constructor's parameters sit in the class header.
varorvalin front of a parameter makes it a field. Without either, it is only a parameter.- All arguments must be given, unless the parameter has a default value.
- Extra constructors use
def thisand must start by calling another constructor.
