Constructors

on waitingforcode.com

Constructors

Scala is known as more concise language than Java. One of points showing this characteristics are classes constructors that can be defined with a single line, alongside of class name definition.

This post presents Scala's constructors more in depth. The first section talks about primary constructor and all its components. The next one explains the role of private constructor. It's followed by a part about auxiliary constructor. The post ends with a section about constructor parameters and subclasses in the context of inheritance.

Primary constructor

The constructors for Scala classes are defined at the same level as class name. Because of that we can think about them as about normal functions prefixed by class keyword instead of def one. In consequence defining constructors in Scala is much shorter than in Java where we need to define not only at least 3 lines for construction method but also much more if we've publicly exposed fields (getters). In Scala defining a constructor is often resumed to:

class TestedClass(constructorParam1: String, constructorParam2: String) {}

The 2 parameters are the values used to build an instance of the object. They remain private for the class as long as they're not prefixed with var or val keywords. The former one automatically makes given field publicly accessible and modifiable. The latter one only exposes the field without the possibility to modify it, as shown in this test case:

"primary constructor" should "create new object instance" in {
  class Person(firstName: String, val lastName: String, var age: Int, val likedMovies: mutable.ListBuffer[String]) {}

  val person = new Person("a", "b", 30, new mutable.ListBuffer[String]())
  // firstName attribute is internal to the constructor. Only the ones marked with val or var keywords
  // are exposed either as setter or setter+getter.

  person.lastName shouldEqual "b"
  person.age shouldEqual 30
  person.age = 31
  person.age shouldEqual 31
  // But beware of container-like objects marked as val - they can change
  person.likedMovies shouldBe empty
  person.likedMovies.append("a")
  person.likedMovies should have size 1
}

By default constructor parameters are private and immutable. By telling "private" I mean here not available from outside because, as you can see in the below test case, that doesn't compile even for 2 instances of the same class:

class Person(private val firstName: String, lastName: String) {
  def compareWithOtherPerson(otherPerson: Person) = {
    otherPerson.firstName == firstName && otherPerson.lastName == lastName
  }
}

The compiler returns an error telling that Error:(29, 21) value lastName is not a member of Person. But if we remove the comparison in lastName, the code works correctly:

"primary constructor private parameter" should "be public within the instance" in {
  class Person(private val firstName: String, lastName: String) {
    def compareWithOtherPerson(otherPerson: Person) = {
      // otherPerson.lastName is not accessible from this point - the parameter is strictly
      // internal to the otherPerson instance !
      otherPerson.firstName == firstName
    }
  }

  val person1 = new Person("a", "b")
  val person2 = new Person("c", "d")
  val isTheSamePerson = person1.compareWithOtherPerson(person2)

  isTheSamePerson shouldBe false
}

Constructor internal parameters, not prefixed with val or var, become eligible to garbage collection as soon as they're not used anymore by the class. But in the other side, if the class uses them to compose another field or takes them as values in a function, internal parameters have much longer lifecycle. It's visible pretty clearly if we decompile both similar classes and compare their compiled code:

class ConstructorClassInternals(param1: Int, param2: Int) {

}

class ConstructorClassInternalsUsed(param3: Int, param4: Int) {
  def sum(): Int = param3 + param4
}

After executing javap -v on both of them we receive:

public class ConstructorClassInternalsUsed
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
 // ...
  #13 = Utf8               ()I
  #14 = NameAndType        #9:#10         // param3:I
  #15 = Fieldref           #2.#14         // ConstructorClassInternalsUsed.param3:I
  #16 = NameAndType        #11:#10        // param4:I
  #17 = Fieldref           #2.#16         // ConstructorClassInternalsUsed.param4:I


  public ConstructorClassInternalsUsed(int, int);
    descriptor: (II)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0
         1: iload_1
         2: putfield      #15                 // Field param3:I
         5: aload_0
         6: iload_2
         7: putfield      #17                 // Field param4:I
        10: aload_0
        11: invokespecial #24                 // Method java/lang/Object."":()V
        14: return

  public ConstructorClassInternals(int, int);
    descriptor: (II)V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=3, args_size=3
         0: aload_0
         1: invokespecial #15                 // Method java/lang/Object."":()V
         4: return

As you can see, numeric parameters for ConstructorClassInternalsUsed class became its internal immutable fields. It's not the case of ConstructorClassInternals where both values are passed as constructor's parameters.

Private constructor

One could ask why the construct needs to be private ? The decision about reducing its visibility is very often limited to the initialization method through factory methods to, for instance, keep the initialization logic for all instances in a single place (e.g. offer a shared cache for class fields) or to enforce class instance uniqueness (singleton design pattern).

To make a class constructor private nothing simpler than precede it with private keyword:

"singleton" should "be enforced with private constructor" in {
  // without private constructor it's still possible to create new instance otherwise than
  // from singleton's factory method
  // It also may enforce the use of factory method to create new instances of given class (if we allow
  // to have multiple instances of the same class)
  class Person private() {}
  object Person {
    private var instance: Option[Person] = None
    def apply(): Person = {
      instance synchronized {
        if (instance.isEmpty) {
          instance = Some(new Person)
        }
        instance.get
      }
    }
  }

  val singletonPerson = Person()
  val singletonPerson2 = Person()

  singletonPerson shouldEqual singletonPerson2
}

Auxiliary constructor

Sometimes the primary construct is not enough because, for instance, of optional presence of some parameters. In this situation we have 2 choices:

  • using default parameters for optional ones:
    "default parameters" should "be used to create new class instance" in {
      class Person(val firstName: String, val lastName: String, val age: Option[Int] = None) {
      }
    
      val personFromAuxiliary = new Person("a", "b")
    
      personFromAuxiliary.firstName shouldEqual "a"
      personFromAuxiliary.lastName shouldEqual "b"
      personFromAuxiliary.age shouldBe empty
    }
    

    Unfortunately this approach doesn't work if we want to allow class instance creation with other types than the ones defined in the constructor.
  • defining auxiliary constructor:
    "auxiliary constructors" should "be used to create new class instance" in {
      class Person(val firstName: String, val lastName: String, val age: Option[Int]) {
        def this(firstName: String, lastName: String) {
          this(firstName, lastName, None)
        }
      }
    
      val personFromAuxiliary = new Person("a", "b")
    
      personFromAuxiliary.firstName shouldEqual "a"
      personFromAuxiliary.lastName shouldEqual "b"
      personFromAuxiliary.age shouldBe empty
    }
    

The rules applying for auxiliary constructor in Scala don't differ from Java. It means that the auxiliary constructor must call a previously defined auxiliary constructor or directly primary constructor. By doing that we ensure that every auxiliary constructor will end up with the invocation of the primary constructor. As you saw in the above example, auxiliary constructors are methods called this and the can take any number of parameters - even those not used in the primary constructor. Auxiliary constructors can be defined for any class, including case class:

"auxiliary constructor" should "be used in case class when the latter is initialized with new operator" in {
  case class PersonWithAuxiliaryConstructor(firstName: String, lastName: String, age: Int) {
    def this(firstName: String) {
      this(firstName, "", -1)
    }
  }
  val personWithAllParams = new PersonWithAuxiliaryConstructor("a", "b", 40)
  val personWithOnlyFirstName = new PersonWithAuxiliaryConstructor("x")

  personWithAllParams.firstName shouldEqual "a"
  personWithAllParams.lastName shouldEqual "b"
  personWithAllParams.age shouldEqual 40
  personWithOnlyFirstName.firstName shouldEqual "x"
  personWithOnlyFirstName.lastName shouldEqual ""
  personWithOnlyFirstName.age shouldEqual -1
}

Another way to add an auxiliary constructor to case class is the overriding of companion object's apply method. However in this approach we can override this method only once. Thus, it's not an universal solution, as we can see in the following code:

"auxiliary constructor" should "be used in case class when the latter is initialized with companion object's apply" in {
  object PersonWithCompanion {
    def apply(age: Int) = new PersonWithCompanion("", "", age)
  }
  case class PersonWithCompanion(firstName: String, lastName: String, age: Int) {}

  val personWithAllParams = PersonWithCompanion("a", "b", 23)
  // What if we want to make lastName optional too, still using an auxiliary constructor
  // and being able to create case class without new operator ? It seems that default arguments
  // are really the best approach to do that
  val personWithDefaults = PersonWithCompanion(32)

  personWithAllParams.firstName shouldEqual "a"
  personWithAllParams.lastName shouldEqual "b"
  personWithAllParams.age shouldEqual 23
}

Inheritance

In inheritance a subclass must define constructor parameters of its parent. In Scala the syntax is a little bit verbose, especially when the parent has more than 2 parameters:

"primary constructor" should "has its parameters overriden in the subclass" in {
  abstract class Person(firstName: String, lastName: String) {}
  class AgedPerson(val firstName: String, lastName: String, val age: Int)
    extends Person(firstName, lastName) {}

  val agedPerson = new AgedPerson("a", "b", 30)

  agedPerson.firstName shouldEqual "a"
  agedPerson.age shouldEqual 30
}

This verbosity can be eliminated with a pattern using class attributes inside a trait:

"trait" should "reduce the number of redeclared parameters in parent constructor" in {
  trait Person {
    val firstName: String
    val lastName: String

    val fullName = s"${firstName} ${lastName}"
  }
  class AgedPerson(val firstName: String, val lastName: String, val age: Int) extends Person {}

  val agedPerson = new AgedPerson("a", "b", 30)

  agedPerson.firstName shouldEqual "a"
  agedPerson.lastName shouldEqual "b"
  agedPerson.fullName shouldEqual "a b"
  agedPerson.age shouldEqual 30
}

An important point to remember with the inheritance is the execution of initialization code in the parent class. The block executes every time when a new instance of child class is created. So if your parent class does some resource-intensive operation not needed by the child, either the inheritance is wrong or simply class instance creation should be put elsewhere (e.g. factory method). The following test case shows that:

"child class" should "execute parent's initialization code" in {
  class Parent(fileToLoad: String) {
    // Imaginary code to load the file from the parameter
    val loadedFile = fileToLoad
  }
  class Child(fileToLoad: String) extends Parent(fileToLoad) {
    // Imagine here we don't need to load the file
  }

  val child = new Child("config.xml")

  child.loadedFile shouldEqual "config.xml"
}

At first glance Scala's constructors look like simple methods preceded with class operator instead of def. However, as shown throughout this post, they have a wide range of features. First, they are able to manipulate parameters visibility. It strongly contributes to reduce the code verbosity since by simple var or val definition we implicitly create getters and eventually setters. Moreover, with private with can reduce the visibility to only given instance of the class. Constructors itself can be private. It enforces the control on the instances and their creation. This creation can be often defined in multiple places with the help of auxiliary constructors. Finally, as a language implementing OOP concepts, Scala lets us to build inheritance trees and here, inevitably, we'll encounter the need to redeclare child class parameters in parent class call.

Share, like or comment this post on Twitter:

Share on: