# Secondary Constructors

When you require several ways to construct an object, named and default arguments are usually the easiest approach. Sometimes, however, you must create multiple overloaded constructors.

The constructor is “overloaded” because you’re making different ways to create objects of the same class. In Kotlin, overloaded constructors are called secondary constructors. The constructor parameter list (directly after the class name) combined with property initializations and the init block is called the primary constructor.

To create a secondary constructor, use the constructor keyword followed by a parameter list that’s distinct from all other primary and secondary parameter lists. Within a secondary constructor, the this keyword calls either the primary constructor or another secondary constructor:

// SecondaryConstructors/WithSecondary.kt
package secondaryconstructors
import atomictest.*

class WithSecondary(i: Int) {
  init {
    trace("Primary: $i")
  }
  constructor(c: Char) : this(c - 'A') {
    trace("Secondary: '$c'")
  }
  constructor(s: String) :
    this(s.first()) {             // [1]
    trace("Secondary: \"$s\"")
  }
  /* Doesn't compile without a call
     to the primary constructor:
  constructor(f: Float) {         // [2]
    trace("Secondary: $f")
  }
  */
}

fun main() {
  fun sep() = trace("-".repeat(10))
  WithSecondary(1)
  sep()
  WithSecondary('D')
  sep()
  WithSecondary("Last Constructor")
  trace eq """
    Primary: 1
    ----------
    Primary: 3
    Secondary: 'D'
    ----------
    Primary: 11
    Secondary: 'L'
    Secondary: "Last Constructor"
  """
}

Calling another constructor from a secondary constructor (using this) must happen before additional constructor logic, because the constructor body may depend on those other initializations. Thus it precedes the constructor body.

The argument list determines the constructor to call. WithSecondary(1) matches the primary constructor, WithSecondary('D') matches the first secondary constructor, and WithSecondary("Last Constructor") matches the second secondary constructor. The this() call in [1] matches the first secondary constructor, and you can see the chain of calls in the output.

The primary constructor must always be called, either directly or through a call to a secondary constructor. Otherwise, Kotlin generates a compile-time error, as in [2]. Thus, all common initialization logic that can be shared between constructors should be placed in the primary constructor.

An init section is not required when using secondary constructors:

// SecondaryConstructors/GardenItem.kt
package secondaryconstructors
import atomictest.eq
import secondaryconstructors.Material.*

enum class Material {
  Ceramic, Metal, Plastic
}

class GardenItem(val name: String) {
  var material: Material = Plastic
  constructor(
    name: String, material: Material    // [1]
  ) : this(name) {                      // [2]
    this.material = material            // [3]
  }
  constructor(
    material: Material
  ) : this("Strange Thing", material)   // [4]
  override fun toString() = "$material $name"
}

fun main() {
  GardenItem("Elf").material eq Plastic
  GardenItem("Snowman").name eq "Snowman"
  GardenItem("Gazing Ball", Metal) eq   // [5]
    "Metal Gazing Ball"
  GardenItem(material = Ceramic) eq
    "Ceramic Strange Thing"
}
  • [1] Only the parameters of the primary constructor can be declared as properties via val or var.
  • [2] You cannot declare a return type for a secondary constructor.
  • [3] The material parameter has the same name as a property, so we disambiguate it using this.
  • [4] The secondary constructor body is optional (although you must still include an explicit this() call).

When calling the first secondary constructor in line [5], the property material is assigned twice. First, the Plastic value is assigned during the call to the primary constructor (in [2]) and initialization of all the class properties, then it’s changed to the material parameter at [3].

The GardenItem class can be simplified using default arguments, replacing the secondary constructors with a single primary constructor.

Exercises and solutions can be found at www.AtomicKotlin.com.