Kotlin Classes and Objects

In this tutorial, we are going to talk about classes, interfaces, polymorphism and inheritance in Kotlin language.

Classes

  • If we want our application to work with types of objects that Kotlin does not have, we can create our own types by defining new classes.
  • Classes are blueprint that allow us to create our own type of objects and define their properties and methods/functions.
  • Constructors, Initializer blocks, getters and setters are the building blocks of classes.

How to define the class

A class is defined using (1) class keyword, (2) an identifier, that will be its name, (3) an optional header and (4) an optional body.

A class in Kotlin

class Book()
{

}
  • The header of a class is the pair of parentheses. The header of a class may contain parameters, but in above example, it does not have any.
  • The pair of curly braces contains the body of the class.

How to instantiate the class

To instantiate the Book class, when can write something like below:

var book = Book()

It is notable that new keyword is not required to instantiate the object of Book class. The pair of parentheses after the type name (class name) is an implicit call to (default) no-argument constructor.

Java classes do not have headers, but Kotlin classes do have the headers. The header is in fact a constructor definition.

Constructors

A constructor is a special member function that is invoked when an object of the class is created primarily to initialize variables or properties. A Constructor runs implicitly when we instantiate an object.

A Constructor contains the code that is required to initialize an object. It runs just before the object is assigned to a reference. Therefore, we have a chance to step in, and do the stuff to make the object ready to use. In general, constructors are used to define an object’s properties and to assign values to them.

Types of Constructors

  • Primary constructor
  • Secondary constructor

The primary constructor is responsible for initializing the class, whereas the secondary constructor is responsible for initializing the class as well as some extra logic.

Primary Constructor

The primary constructor is written at the header part of the class, like the one we’ve seen above. We can only have one primary constructor for a class.

A class with primary constructor

class Book constructor (val _id:String, val _name : String)
{
    val id = _id
    val name = _name
}

In the above code snippet, the Book class is define with primary constructor using constructor keyword, though constructor keyword is optional and can be omitted.

fun main()
{
    val book = Book ("B101","Learn Kotlin")
    println ("The Book ${book.Name}'s ID is : ${book.ID}")
}

class Book constructor (val ID:String, val Name : String)


/* OUTPUT

The Book Learn Kotlin's ID is : B101

*/

In the above example, the book object is initialized with implicit constructor, argument B101 is set to book id and 2nd argument “Learn Kotlin” will be assigned to the book name field.

Primary Constructor with init block

fun main()
{
    val book = Book ("B101","Learn Kotlin")
    println ("The Book ${book.Name}'s ID is : ${book.ID}")

}

class Book constructor (val ID:String, val Name : String)
{
    // Initializer Block
    init 
    {
        println("init block for primary constructor")
        println("Book ID : $ID")
        println("Book Name : $Name")
    }
}


/* 
OUTPUT

init block for primary constructor
Book ID : B101
Book Name : Learn Kotlin
The Book Learn Kotlin's ID is : B101

*/

Above is an example of primary constructor with an init block. Init is a special block for primary constructor, executed when the primary constructor is called.

Secondary Constructor

When a constructor is written inside a class, it is called secondary constructors, just like the way we used to do in Java. We may have multiple secondary constructors in a Kotlin class.

fun main()
{
    val teacher = Teacher("Ravi R. Oza")

    println("Secondary Constructor Demo\n")
    println ("Hello Master ${teacher.Name} ")

}

class Teacher
{
    var Name : String
    constructor(Name: String)
    {
        this.Name = Name
    }
}

/* 
OUTPUT

Secondary Constructor Demo

Hello Master Ravi R. Oza

*/

In the above example class Teacher has a secondary constructor for the field Name.

Another example with Two Secondary constructors

fun main()
{
    val teacher1 = Teacher("101","Ravi R. Oza")
    val teacher2 = Teacher("102")

    println("Secondary Constructor Demo\n")
    println ("Hello Master ${teacher1.Name} ")
    println ("Hello Master ${teacher2.Name} ")

}

class Teacher
{
    var ID : String = ""
    var Name : String = ""

    constructor(ID: String) : this(ID,"XYZ")
    {
        this.ID = ID
    }
    constructor(ID: String, Name: String)
    {
        this.ID = ID
        this.Name = Name
    }
}

/* 
OUTPUT

Secondary Constructor Demo

Hello Master Ravi R. Oza 
Hello Master XYZ 

*/

In the above example, the first secondary constructor accepts only one argument, while the second constructor accepts two. Observe the first secondary constructor, it has only argument, but the later half this is used to invoke the another secondary constructor that accepts two arguments, in this invocation second argument has a default value of “XYZ”.

Secondary Constructor with init block

Below is an example of a Secondary constructor with multiple init blocks, each init block will be invoked in sequence as they are written. Thereafter the secondary constructor is going to invoke.

fun main()
{
    val teacher1 = Teacher("101","Ravi R. Oza")
    val teacher2 = Teacher("102")


    println("\nSecondary Constructor Demo\n")
    println ("Hello Master ${teacher1.Name} ")
    println ("Hello Master ${teacher2.Name} ")

}

class Teacher
{
    var ID : String = ""
    var Name : String = ""

    init {
        println("init block : 1")
    }
    init {
        println("init block : 2")
    }
    constructor(ID: String) : this(ID,"XYX")
    {
        this.ID = ID
    }
    constructor(ID: String, Name: String)
    {
        this.ID = ID
        this.Name = Name
    }
}

/* 
OUTPUT

init block : 1
init block : 2
init block : 1
init block : 2

Secondary Constructor Demo

Hello Master Ravi R. Oza 
Hello Master XYX  

*/

Kotlin Getters and Setters

  • Before diving into getters and setters, make sure to introduce yourself with Kotlin classes and objects.
  • Getters are used in programming to retrieve the value of a property. Setters, on the other hand, are used to set the value of a property.
  • Getters and setters are optional in Kotlin and are generated automatically if they are not created in the program.

Implicit getters and setter example

// www.raviroza.com
// 02-Feb-22, 4.46 pm
class Computer
{
    var name = "no name"
}
fun main() {

    var pc = Computer()

    // implicit setter for name field
    pc.name = "IBM 186"

    // implicit getter for name fields
    println("${pc.name}, is an Old Computer")
}

/*

OUTPUT
IBM, is an Old Computer

*/

Explicit getters and setter for properties (custom Accessors)

// www.raviroza.com
// 02-Feb-22, 4.46 pm
class Computer
{
    var name = "no name"


    var model = ""
    // explicit getter
    get()
    {
        return field
    }
    // with short declaration
    // get() = field

    // explicit setter using Backing field
    set(value)
    {
        field=value
    }
}
fun main() {

    var pc = Computer()

    // implicit setter for name field
    pc.name = "IBM "
    pc.model = "186"

    // implicit getter for name fields
    println("${pc.name} -> ${pc.model}, is an Old Computer ")
}

/*

OUTPUT
IBM  -> 186, is an Old Computer

*/

Value and Field Identifiers

Two identifiers, field and value are used in the above program.

  • Backing Field (field): It enables the storage of the property value in memory. When we initialize a property with a value, that value is written to the property’s backing field.
  • Value: The value parameter contains the value to which a property has been assigned.

Abstract class

A class, as well as some or all of its members, can be declared abstract. There is no implementation of an abstract member in its class. We do not need to use open to annotate abstract classes or functions. abstract keyword is required to declare abstract class.

When we mark a class as abstract, it becomes implicitly open as well.

Example to demonstrate abstract class:

// www.raviroza.com
// 03-Feb-22, 6.08 pm

abstract class MyPolygon {
    abstract fun draw()
    abstract fun calcuate()
    fun other()
    {
        println("regular method in abstract class")
    }
}

class MyRectangle : MyPolygon() {
    override fun draw() {
        // draw the rectangle
        println("implementation of abstract method draw")
    }

    override fun calcuate() {
        TODO("Not yet implemented")
    }
}

fun main() {

    var rect = MyRectangle()
    rect.draw()
    rect.other()
}

/*

OUTPUT
implementation of abstract method draw
regular method in abstract class

*/

Like Java, all the abstract methods of base class must be overridden in child class, otherwise child class also needs to be declared as abstract. Though, abstract classes still may contain non abstract methods as shown above.