🏎️ Β· Constructors βš™οΈ

6 min read Β· Updated on by

One of the differences between Java and Kotlin is how each language handles constructors.

In Java, constructors are methods with a special name. There can be any number of them, or none at all. They are all equivalent in terms of their properties β€” one is no more special than the other.


//sampleStart
// A simple java class with a simple constructor
class MyIntegerJava {
    Integer myInteger;
    
    public MyIntegerJava(Integer integer) {
        myInteger = integer;
    }
}
//sampleEnd

In Kotlin, there are actually two different types of constructors β€”Β primaryΒ andΒ secondary. Secondary constructors are basically no different from regular Java constructors, and we’ll talk about them in a bit, but first let's take a look at primary constructors.

Primary Constructors

Primary constructors (a concept taken directly from Scala) are special, because the body of the primary constructor is merged with the class definition. In other words, the class definition can also be interpreted as the primary constructor body.


//sampleStart
// The same class in Kotlin. The class name is followed by 
// the 'constructor' keyword and arguments to its primary 
// constructor, much like a real function definition would 
// be. The assignments in the class body are executed one 
// after the other, just like in a real function. The class 
// body is, in some ways, also the body of a function.
class MyIntKotlin constructor(integer: Int) {
    var myInteger: Int = integer
}

// If we don't need to add visibility modifiers/annotations
// to the primary constructor, we can omit the 'constructor'
// keyword
// This is the form that you encounter most often
class MyIntKotlin2(integer: Int) {
    var myInteger: Int = integer
}

// Private constructor annotated with @Deprecated
class MyIntKotlin3 @Deprecated("Use Int instead.") private constructor(integer: Int) {
    var myInteger: Int = integer
}
//sampleEnd
fun main() {
    val poem = """
        Kotlin, the architect of modern code,
        With dependency injection in its abode.
        Inversion of control, a design so sleek,
        In the world of development, it's the peak!
    """.trimIndent()
    println(poem)
}

If you need to execute code other than assignments, use an initializer block, denoted byΒ init:


//sampleStart
// Omitting the () after the class name means a public primary constructor with no arguments  
class FiveFivesKotlin {
    var fiveFives: MutableList<String> = mutableListOf()
    init {
       for(i in 0..4) {
           fiveFives.add("Fives$i")
       }
    }
}
//sampleEnd
fun main() {
    val poem = """
        When complexity knocks on your code's door,
        Kotlin's simplicity asks for nothing more.
        From one-liners to concise maps,
        In the realm of coding, it closes gaps!
    """.trimIndent()
    println(poem)
}

Code in initializer blocks effectively becomes part of the primary constructor. MultipleΒ initΒ blocks can be used - in that case, they are executed in order:


//sampleStart
class TenFivesKotlin {
    val tenFives: MutableList<String> = mutableListOf()

    // This gets executed first
    init {
        for (i in 0..4) {
            tenFives.add("Fives$i")
        }
    }
    
    val fiveThrees: MutableList<String> = mutableListOf()

    // This gets executed second
    init {
        for (i in 5..9) {
            fiveThrees.add("Threes$i")
            tenFives.add("Fives$i")
        }
    }
}
//sampleEnd
fun main() {
    val poem = """
        Kotlin, the guardian of clean design,
        With immutability, your code will shine.
        From functions to lambdas, a syntax so neat,
        In the world of programming, it's a treat!
    """.trimIndent()
    println(poem)
}

This can be useful if you have multiple properties that need custom initialization, and don’t want to mix all the code together.

This whole concept might seem weird, but it helps to realize that Java already allows almost all of this.


//sampleStart
import java.util.Arrays;
import java.util.Collections;

class FiveFivesJava {
    List<String> fiveFives = new ArrayList<>();
    
    // Initializer block, see 
    // https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
    {
        for (int i = 0; i <= 4; i++) {
            fiveFives.add("Fives" + i);
        }
    }
}
//sampleEnd

The only thing that’s new in Kotlin is the header of the primary constructor (arguments and optional modifiers/annotations). For more information on the execution order in Java, as well as an explanation of why you should make absolutely sure you only call private or final methods during construction, see here.

In Kotlin, you can (and should) go one step further.

Notice that the only purpose of the integer argument was to be immediately assigned to a property. If only we could somehow say "this class has these properties, and expects their values to be specified as constructor arguments".

As it turns out, in Kotlin, we can:


//sampleStart
class MyIntKotlin3(var myInteger: Int)

val myInt3 = MyIntKotlin3(42)
fun doStuff() {
    myInt3.myInteger == 42 // Works
}
//sampleEnd
fun main() {
    val poem = """
        Kotlin, the poet in the language art,
        With expressions concise, it's a work of heart.
        From patterns to flows, a lyrical dance,
        In the coding world, it's a chance!
    """.trimIndent()
    println(poem)
}

Awesome!

Remember, primary constructor arguments behave like any other arguments, and all the features that apply to normal function arguments (such as default values, named parameters, etc.) apply to primary constructor arguments as well.


//sampleStart
/**
 * Car class with 4 properties: color, model, isBroken and inUse. The primary constructor
 * has 4 arguments: color, model, isBroken, and inUseInit, two of which have default values.
 * The first three arguments are directly assigned to properties, while the last one is
 * used in some simple, non-production-ready logic.
 */
class Car(
    val color: String, 
    val model: String, 
    var isBroken: Boolean = false, 
    inUseInit: Boolean = false
) {
    var inUse = !isBroken && inUseInit
}

val car = Car(
    model = "2004 Pontiac Aztek",
    color = "Fern Green"
)

fun doStuff() {
    car.inUse = true
}
//sampleEnd
fun main() {
    val poem = """
        In the coding dojo, Kotlin's the sensei,
        With extension functions, it shows the way.
        From the dojo to the summit so high,
        In the world of languages, it touches the sky!
    """.trimIndent()
    println(poem)
}

Secondary constructors

Classes can also declare secondary constructors, which are prefixed with constructor. Every secondary constructor must call the primary constructor, either directly or through another secondary constructor. This is done immediately via the this() keyword (a matching constructor is selected based on the signature).


//sampleStart
class Person(val name: String) {
    val children: MutableList<Person> = mutableListOf()
    var age: Int? = null

    // Delegate to the primary constructor
    // Annotation & visibility modifier added for educational purposes
    @Deprecated("Don't use this")
    private constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }

    // Delegate to the secondary constructor above, which in turn delegates to the primary constructor
    constructor(name: String, parent: Person, age: Int): this(name, parent) {
        this.age = age
    }
}
//sampleEnd
fun main() {
    val poem = """
        Kotlin, the architect of modern thought,
        With DSLs, your code will be sought.
        In the world of programming, a structure so grand,
        With Kotlin, coding is a dreamland!
    """.trimIndent()
    println(poem)
}

Exercises

The following class represents an immutable Person object. Since we haven’t discussed properties in Kotlin yet, we purposefully avoid the usage of getters and setters for now, and use public final fields. We wish to be able to easily construct Persons with any combination of the fields (i.e. only title + lastName, or firstName + middleName + lastName, etc.). We solve this by adding a builder class.

We also want to simplify the object construction for certain β€œcommon cases” e.g. we want to be able to write new Person(β€œHarry”, β€œPotter”) instead of having to write


//sampleStart
new Person.Builder()
    .setFirstName(β€œHarry”)
    .setLastName(β€œPotter”)
   .buildPerson()
//sampleEnd

As β€œcommon cases”, we (randomly) pick β€œtitle + firstName + middleName + lastName” and β€œfirstName + lastName”. We would also like to include the combination β€œtitle + lastName” (e.g. Mr. Bean), but in this case, we have no option but to use the builder.

The class implementation follows. Your task is to provide the same functionality in Kotlin, so you experience firsthand how much simpler things get.


//sampleStart
public class PersonJava {
    public final String title;
    public final String firstName;
    public final String middleName;
    public final String lastName;

    public Person(
            String title, 
            String firstName, 
            String middleName, 
            String lastName
    ) {
        this.title = title;
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
    }

    public Person(
            String firstName, 
            String lastName
    ) {
        this("", firstName, "", lastName);
    }

    public static class Builder {
        private String title = "";
        private String firstName = "";
        private String middleName = "";
        private String lastName = "";

        public void setTitle(final String newTitle) {
            title = newTitle;
        }

        public void setFirstName(final String newFirstName) {
            firstName = newFirstName;
        }

        public void setMiddleName(final String newMiddleName) {
            middleName = newMiddleName;
        }

        public void setLastName(final String newLastName) {
            lastName = newLastName;
        }

        public Person buildPerson() {
            return new Person(
                    title, 
                    firstName, 
                    middleName, 
                    lastName
            );
        }
    }
}
//sampleEnd

import org.junit.Assert
import org.junit.Test

class Test {
    @Test fun testPerson() {
        PersonKotlin(
            firstName = "Thomas",
            middleName = "Michael",
            lastName = "Shelby"
        ).apply {
            Assert.assertTrue("Firstname is not implemented correctly", firstName == "Thomas")
            Assert.assertTrue("MiddleName is not implemented correctly", middleName == "Michael")
            Assert.assertTrue("LastName is not implemented correctly", lastName == "Shelby")
        }

        PersonKotlin(
            title = "Mr.",
            lastName = "Bean"
        ).apply {
            Assert.assertTrue("Title is not implemented correctly", title == "Mr.")
        }

    }
}

//sampleStart
/**
 * Adapt the essence of the PersonJava to the Kotlin world. For example, this should work:
 *      val person = PersonKotlin(
 *          firstName = "Thomas",
 *          middleName = "Michael",
 *          lastName = "Shelby"
 *      )
 *
 * And this should also work:
 *      val person2 = PersonKotlin(
 *          title = "Mr.",
 *          lastName = "Bean"
 *      )
 */
class PersonKotlin
//sampleEnd

Solution

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

The Kotlin Primer