1. Overview

Constructors in Scala describe special methods used to initialize objects. When an object of that class needs to be created, it calls the constructor of the class. It can be used to set initial or default values for object attributes.

2. Constructors

Constructors define the basic blueprint of a class and may enforce the existence of some values for object creation:

class Employee(name : String, email : String)

We just defined a class Employee and a constructor that takes two arguments: a name of type String and an email of type String. By creating a constructor, we’ve enforced that every employee must have a name and email. Anyone who wants to create an instance of an Employee must supply these values.

Now, let’s say we extend the class also to have a role associated with it. The role could be either be 0 for a worker or 1 for a manager and so on. We could easily add another argument to the constructor:

 class Employee(name : String, email : String, role : Int)

But what if we don’t want anyone to have the ability to create an employee with any role or supply an invalid value for a role. Anyone that uses this constructor can assign any role to any employee, and we don’t want that.

Instead, we want to expose functionality to create an employee without having to supply a role. We can do this using private and protected constructors.

3. Private Constructors

We use private constructors when we want to prevent the instantiation of our class by directly calling the new keyword, but instead, provide other means to instantiate the class.

Some constructors have a quite complex structure that’s crucial to the functionality of the class. Designers of these constructors often decide to make these constructors private and instead supply easy-to-read methods to create instances of these classes.

We can’t extend a class with a private constructor.

A perfect example is the Vector class in Scala. The constructor is private, so we can’t instantiate the class via the new keyword. Instead, we use the Vector#apply method.

Going back to our previous example, we can make the Employee class private by adding the private keyword after the class name:

class Employee private (name : String, email : String, role : Int)

This way, whenever we try to instantiate an Employee with the new keyword, we get an error from the Scala REPL:

scala> val employee = new Employee("John Doe", "[email protected]")
<console>:12: error: constructor Employee in class Employee cannot be accessed in object $iw
       val employee = new Employee("John Doe", "[email protected]")

By doing this, we’ve made our constructor private and have thus ensured that the new keyword isn’t used to create an instance of the Employee class.

3.1. Instance Creation

Now that we’ve made our constructor private, the first question that comes to mind is how to instantiate the class. One way to do this is to define a companion object that we can use to create instances of the class following the singleton pattern.

Companion objects have the ability to access private constructors of their corresponding class. 

We can easily define a companion object for the Employee class:

class Employee private(name: String, email: String, role: Int)
object Employee {
  def createWorker(name: String, email: String): Employee = new Employee(name, email, 0)
  def createManager(name: String, email: String): Employee = new Employee(name, email, 1)
}
val worker = Employee.createWorker("John Doe", "[email protected]")
val manager = Employee.createManager("John Smith", "[email protected]")

This way, rather than using the new keyword, we can easily call Employee#createWorker or Employee#createManager.

4. Protected Constructors

We explained in our previous example that classes with a private constructor could not be extended. But what if we want to maintain constructor privacy and still be able to extend that class?

We can do this using protected constructors.

Protected constructors are similar to private constructors, in that their classes cannot be instantiated using the new keyword, but they can be extended.

In our example, rather than using methods on companion objects, we can define Worker and Manager classes that extend the Employee class:

class Employee protected(name: String, email: String, role: Int)
class Worker(name: String, email: String) extends Employee(name, email, 0)
class Manager(name: String, email: String) extends Employee(name, email, 1)

val worker = new Worker("John Doe","[email protected]")
val manager = new Manager("John Smith","[email protected]")

Using a protected constructor, we’ve maintained our constructor privacy but enabled our class to be extended. We can easily use the new keyword to create instances of the Worker and Manager classes, but not the Employee class.

5. Conclusion

In this article, we’ve seen how to define constructors in Scala and restrict access to these constructors. We also discussed the difference between constructors marked as private and those marked as protected.

As usual, the source code can be found over on GitHub.

guest
0 Comments
Inline Feedbacks
View all comments