Instruction

Constructor

In the previous lesson, you saw how the class is defined with properties to be set during instantiation. Now go back to the same example and look at it with a magnifying glass:

class Food(var name: String, var price: String)

Under the magnifying glass, you’ll see an invisible, or as computer languages define it, implicit, function definition.

Note: In computer languages, some keywords or statements are simplified or, more often, omitted. It’s called implicit usage or default behavior. It may seem unreasonable when you first start learning a computer language. However, as you learn more concepts, you’ll see that implicit statements save time for both typing and understanding the code.

In the example above, you actually see this:

class Food constructor(var name: String, var price: String)

constructor is a special function and keyword used to declare a class’s behavior during initialization. The constructor, embedded in the class definition, is called a primary constructor and is limited to initializing class properties or collecting variables available only during the class initialization stage. Later in this lesson, you’ll see how to use this to your advantage.

The primary constructor is the one defined in the class header. You should use it to set the initial state of the class, define values of the class properties.

As with any other function, you can limit constructor visibility by using visibility modifiers.

class Food private constructor(var name: String, var price: String)

In this example, the constructor is private, which means it can’t be accessed from outside the class, and the class can’t be created or instantiated. The private modifier is not the only one we can use here. You can use protected, internal, and public as well. I’ll stop you here and ask you to do your own exploration to understand the benefit of this class declaration and usage of different visibility modifiers.

Secondary Constructor

Here’s a question: Can you have more than one constructor? Yes, that’s actually the primary purpose of the constructor keyword since you don’t see it in the class header. You can have as many constructors as you need, but try to be reasonable! Secondary constructors are used to provide additional ways to create an instance of the class with different data sets. For example when class takes measurements in inches and centimeters, you can have two constructors, one for each unit of measurement, but there’s little value of two constructors that take measurements in meters and kilometers.

All the constructors not defined in the class header are called secondary constructors.

I hope you still enjoy Food…Add following code to the Kotlin Playground

class Food {
  val name: String
  var price: String = "1.0"
  var origin: String = "US"

  constructor(name: String) {
    this.name = name
  }
}

You have defined a secondary constructor, well done! But did you define a primary constructor as well? Let’s break down the first line:

class Food {

The primary constructor is not defined here. To define it, you should add () after Food like this:

class Food() {

Go ahead and make the change above. You’ll see that code is not working anymore and two errors appear:

* Property must be initialized or be abstract
* Primary constructor call expected
  • The first one means that Kotlin requires that all properties are initialized in the constructor. the value of the name property would be unknown if we would use primary constructor Food().
  • The second error means that Kotlin requires that the primary constructor is called when the class is instantiated by secondary constructor.

Note: The primary constructor should always be called. That’s why every secondary constructor must have :this() in the definition. The only exception is when the primary constructor is not defined, then the secondary constructor should initialize all properties and not call the primary constructor.

Replace the Food class with the following code:

//1
class Food(val name: String) {
  var price: String = "1.0"
  var origin: String = "US"

  //2
  constructor(name: String, price: String) : this(name) {
    this.price = price
  }
}

You fixed the error with the following changes:

  1. Make primary constructor initialize the name property by making it a constructor parameter instead of defining it in the body.
  2. Make secondary constructor to call the primary constructor by using : this(name) after the constructor definition.

But what’s the benefit of the secondary constructor? This constructor can have code that will execute during class instantiation, not only the behavior of the properties.

Add one more line to the secondary constructor in the Food class:

  constructor(name: String, price: String) : this(name) {
    println("Secondary constructor used")
    this.price = price
  }

Now create some code in the main() function and run it:

fun main() {
  // one-argument secondary constructor
  var tomato = Food("Tomato")
  println(tomato.name) // Tomato
  println(tomato.price) // 1.0
  println(tomato.origin) // US

  // two-argument secondary constructor
  val tomato2 = Food("Tomato", "2.0") // Secondary constructor used
  println(tomato2.name) // Tomato
  println(tomato2.price) // 2.0
  println(tomato2.origin) // US
}

This way you can get confirmation that the secondary constructor was used only for the tomato2, not for the tomato class instance and tomato was created using the primary constructor with default values for properties.

Initializer Block

What if you need to execute some code during class initialization, for example, initialize property using some complex logic, or check or validate constructor parameters? Apart from the primary constructor, which can be used only to set properties but not code, there’s another language construct you can use. Initializer blocks are curly-braced blocks inside the class body and are declared with the init keyword. The code inside that block will execute during initialization, too.

Add this code to the Food class:

  init {
    println("Init block in action")
    origin = "UK"
  }

Can there be more than one initializer block? Yes! But how are those initializations ordered? Here’s a breakdown. You have the:

  • Primary constructor
  • Secondary constructor
  • multiple property initializations
  • multiple init blocks

During the initialization of an instance, the initializer blocks execute in the order they appear in the class body interleaved with the property initializers.

Initializer blocks are continuations of the primary constructor. If the secondary constructor is invoked, the primary constructor is always invoked first, and by extension the properties initializations, the initializer block and only then the secondary constructor is called.

Note: When there’s no primary constructor, it’s treated as an “empty” constructor, and initializer blocks are appended anyway and executed in exactly the same order.

The order of initialization is then:

  • Primary constructor (even if it’s implicit)
  • Properties and init blocks in the order of appearance
  • Secondary constructor

Now, slightly modify the class and change the code for the price property:

var price: String = "1.0".also {
  println("Setting the price to $it")
}

Then, create some more code in the main function:

  // init block
  tomato = Food("Tomato", "2.0")
  println(tomato.name) // Tomato
  println(tomato.price) // 2.0
  println(tomato.origin) // UK

Note: Notice that you’re reusing variable tomato and not defining the new variable, so var is omitted. If you’re fond of a clean-sheet approach, we’d encourage you to fix the code and let us know.

It’ll print statements in the following order:

Setting the price to 1.0
Init block in action
Secondary constructor used

Objects

We tried to avoid using the term object for class instances because the object has a special meaning in Kotlin. object is a reserved keyword used for object expressions and object declarations.

The object expressions create objects of anonymous classes, that is, classes that aren’t explicitly declared with the class declaration. The object declarations define classes following the Singleton pattern. For example:

object Box {
  var size: String = "0"
  val type = "Cardboard"
}

Singleton patterns means that a class is constructed in a way where there’s always one instance of this class present, and you can’t create another one simply because there’s no constructor available. It exists, but it’s not visible to consumers.

As you can see in the example, you can declare variables mutable and immutable. To access them, you should use the class name as a qualifier:

Box.size = "3"
println(Box.size) // 3
println(Box.type) // Cardboard

If you recall, you used the same syntax in the previous lesson to use the properties of companion object. In this case, the Box object is a companion object in itself. More on the topic of object will be covered in the next Module.

See forum comments
Download course materials from Github
Previous: Introduction Next: Demo