Start learning free

LESSON 182 OF 348 · OBJECT-ORIENTED PROGRAMMING

Constructors in Kotlin: Primary and Secondary

Lesson Read the full explanation

TL ; DR:

  • A constructor is a special method that runs automatically when you create an object. It's used to set up the object's initial state.


  • In Kotlin, a constructor sets up the object's fields when it is created:

    class Dog {
        var name: String
        var age: Int
    
        constructor(name: String, age: Int) {
            this.name = name
            this.age = age
        }
    }
    

  • Then create objects with arguments: Dog("Rex", 4) — the constructor fills in the fields for you.



Full lesson:

In the previous lesson you set fields after creating the object:

val dog = Dog()
dog.name = "Rex"
dog.age = 4

That works, but it's noisy and easy to forget. Constructors let you initialize the fields the moment Dog(...) is called.


Defining a constructor

A Kotlin constructor has two distinguishing features:

  • It is declared with the constructor keyword inside the class.

  • It has no return type — not even Unit.

constructor(name: String, age: Int) {
    this.name = name
    this.age = age
}

The this keyword

The parameter name and the field name have the same identifier. Without this, both sides of name = name would refer to the parameter (the local one wins). this is a built-in reference to "the current object", so this.name is the field, and bare name is the parameter.


Calling the constructor

val rex  = Dog("Rex", 4)
val luna = Dog("Luna", 7)

By the time these lines finish, rex.name is already "Rex", rex.age is 4, and the same for Luna.


The default constructor

If you don't write any constructor, Kotlin provides a hidden no-arg one for free — that's why Dog() worked in earlier lessons. The moment you define your own constructor, that freebie disappears: from now on you must call Dog with the arguments you specified.


Primary constructors:

The form above is a secondary constructor. In everyday Kotlin you will more often see the primary constructor, declared right in the class header. Marking a parameter val (or var) there also creates the property — it declares, stores, and exposes the value in one line:

class Dog(val name: String, var age: Int)

val rex = Dog("Rex", 4)
println(rex.name) // Rex

That single line does the same work as the field declarations plus the constructor block above.


Your task

The Dog class already has the name and age fields. Add a constructor that takes a name: String and an age: Int and assigns them using this. Then in mainFunction, create two dogs:

val rex  = Dog("Rex", 4)
val luna = Dog("Luna", 7)

Expected output:

Rex is 4
Luna is 7
STEP 1 OF 2
    Ctrl + Enter to check

    KOTLIN · OBJECT-ORIENTED PROGRAMMING

    Constructors in Kotlin, explained

    What a constructor does

    A constructor runs when you create an object and sets up its initial state. Without one, you would create an empty Dog and then fill in its name and age line by line, which is noisy and easy to forget. With a constructor, the call Dog("Rex", 4) produces a dog that is ready to use.

    The primary constructor

    Kotlin's usual way is the primary constructor, written in the class header right after the name:

    class Dog(var name: String, var age: Int)
    

    Putting var or val before a parameter does two jobs in one: it takes the argument and it declares a property of the same name, initialized with that argument. This single line replaces a class with two property declarations plus a constructor that assigns them. Use var for a property that may change later and val for one that must not. A class with no body needs no braces.

    Create objects by passing the arguments:

    val rex = Dog("Rex", 4)
    val luna = Dog("Luna", 7)
    

    By the time each line finishes, rex.name is "Rex" and rex.age is 4, and the same holds for Luna.

    Parameters without val or var

    A primary constructor parameter without val or var is only an argument. It is available while the object is built, in property initializers and init blocks, but it does not become a property and methods cannot see it. This matters for inheritance later, and it is a common slip: in class Dog(name: String), a method that reads name fails with unresolved reference 'name'.

    Secondary constructors and init blocks

    A class can also declare constructors with the constructor keyword inside its body. They have no return type, not even Unit. This form needs the properties declared separately, and it uses this to tell the property from the parameter that shares its name:

    class Bird {
        var name: String
        var age: Int
    
        constructor(name: String, age: Int) {
            this.name = name
            this.age = age
        }
    
        constructor(name: String) : this(name, 1)
    }
    

    Inside the constructor, a bare name is the parameter, because the local name wins. this is the current object, so this.name is the property. The second constructor delegates with : this(name, 1), which calls the first one and fills in a default age. With a primary constructor present, every secondary constructor must delegate to it this way.

    When the primary constructor needs more than assignments, put the extra code in an init block. It runs as part of construction, after the properties declared above it are set:

    class Cat(val name: String, age: Int) {
        val label: String
        init {
            label = "$name ($age)"
        }
    }
    

    Often you do not need a secondary constructor at all. Default values give the same effect: class Fish(val name: String, val age: Int = 0) lets you write Fish("Nemo"), and named arguments let you write Fish(age = 2, name = "Dory").

    The default constructor

    A class with no constructor declared gets a hidden one with no parameters. That is why Dog() worked in earlier lessons. As soon as you declare a constructor with parameters, that free one is gone. After class Dog(var name: String, var age: Int), the call Dog() fails with no value passed for parameter 'name' and another error for age. Give the parameters default values, or add a no-argument secondary constructor, if you still want Dog() to work.

    The exercise

    The starter code prints rex.name, rex.age and the same for luna but defines neither the class nor the variables. There are two steps. First, declare Dog with a primary constructor whose name: String and age: Int are var parameters. Second, create the dogs in mainFunction:

    class Dog(var name: String, var age: Int)
    
    class Solution {
        fun mainFunction() {
            val rex = Dog("Rex", 4)
            val luna = Dog("Luna", 7)
            println(rex.name + " is " + rex.age)
            println(luna.name + " is " + luna.age)
        }
    }
    

    It prints:

    Rex is 4
    Luna is 7
    

    The + between a String and an Int works because the left side is a string; Kotlin converts the number to text.

    Common mistakes

    • Forgetting var or val. class Dog(name: String, age: Int) compiles, but rex.name then fails, since no property exists.
    • Swapped arguments. Dog(4, "Rex") does not compile, because the types are checked against the parameter order.
    • Calling Dog() after adding parameters. The no-argument constructor no longer exists unless you provide it.
    • Writing new. Constructors are called like functions: Dog("Rex", 4).

    Key takeaways

    • The primary constructor lives in the class header; val or var on a parameter creates a property.
    • Secondary constructors use the constructor keyword and must delegate to the primary one with this(...).
    • Use this.name to reach a property that a parameter shadows.
    • Declaring any constructor with parameters removes the free no-argument constructor.
    • Default parameter values often replace the need for several constructors.
    Keep going with KotlinAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free