Building blocks of Kotlin modern development: An intro to classes and objects

Building blocks of Kotlin modern development: An intro to classes and objects

In this article, we will go through the following key points:

  1. Introduction

  2. Key Definitions

  3. Understanding the class keyword

  4. Instantiating Objects

  5. Conclusion

Introduction

Object-oriented programming(OOP) is a programming paradigm that organizes code around objects.

This article dives into the core concepts of defining and utilizing classes and objects in Kotlin, providing essential knowledge to create and work with classes and objects effectively within the Kotlin ecosystem. Let's dive in and uncover some of the secrets behind these essential tools that can help you create robust and efficient code.

We would start by comparing the OOP paradigm to a toolbox analogy. Imagine a toolbox containing varying tools such as hammers, pliers, screwdrivers, nails, etc. Each tool possesses distinct features, attributes, and functionalities that characterize them. Similarly, in an object-oriented programming (OOP) context, similar objects should be modeled and behave identically.

An object is modeled around state. State can be best described as a set of data, in the case of our toolbox, the attributes or qualities of a tool, such as its metal composition, possession of a handle, etc.) and the methods used to manage that data (actions you can perform with the tool) e.g. canHit(), canPullNailOut(), e.t.c.

Kotlin is a high-level, cross-platform programming language that employs an object-oriented construct to define classes' structure, behaviors, and interactions. By incorporating methods and properties in a class, Kotlin presents the developer's toolbox, allowing for easy customization of code with simple syntax.

Key Definitions

Before we get in-depth on how to create Kotlin objects, let's have a fundamental understanding of the two main concepts.

  1. Class

  2. Object

A class is a template or blueprint for constructing objects. It defines the attributes and functions that the class object will have.

An object is a single instance (occurrence) of a class. It is merely a representative or an example of a class, reflecting its traits and behaviour. It includes state, which refers to the properties that describe the object, and behaviour, which simply refers to the actions that the object can carry out.

To the left of the image above is a blueprint/specification for a SuperHero class. This blueprint defines the properties that every SuperHero class can have and the actions (methods) they can perform.

To the right of the image are instances/examples of SuperHero objects with the same properties as their class.

In Kotlin, classes are defined with the class keyword. To construct a basic class without any properties, we simply use the class keyword followed by the class name.

Example 1:

class BasicSuperHero{}

Because there are no properties for this class, we may remove the curly brackets and define it as follows:

Example 2:

class BasicSuperHero

As demonstrated in the example below, we can choose to go one step further and develop an AdvancedSuperHero class containing both properties and functions:

This can be done in two ways:

i. Declare the properties in the constructor and initialize them in the class body.

Example 3:

class AdvancedSuperHero1(superHeroName: String, superHeroPower: String, superHeroGroup: String, superHeroPrimaryVillain: String) {
    val name = superHeroName
    val power = superHeroPower
    val group = superHeroGroup
    val primaryVillainName = superHeroPrimaryVillain

    fun fightCrime() {
        println("$name is using $power to fight crime")
    }
}

ii. Declare and initialize properties in the constructor using the var or val keywords.

  • The val keyword is used in declaring an immutable variable (variables that cannot be reassigned; the value never changes)

  • The var keyword is used in declaring a mutable variable (variables that can be reassigned; the value can change)

Example 4:

class AdvancedSuperHero2(val heroName: String, val superPower: String, val superHeroGroup: String, val primaryVillain: String) {
    fun fightCrime() {
        println("$heroName is using $superPower to fight crime")
    }
}

When properties are declared using the var or val keyword, as in Example 4, the property is declared inside the class and is accessible within member functions. In Example 3, on the other hand, the properties are viewed as just parameters only accessible during class initialization; they are not accessible from within member functions or nested classes.

Instantiating Objects

From the examples above, we have specified the blueprint for creating our SuperHero classes. We have also stated:
i. The properties/attributes (heroName, superPower, superHeroGroup, primaryVillain) that every superhero we wish to build should possess.

ii. The actions (fightCrime()) that each SuperHero ought to be able to perform.

Having established that an object is an instance of a class, we can create objects for each class as shown below:

val peterParker = BasicSuperHero()
//Represents an instance of BasicSuperHero class above
val spidey = AdvancedSuperHero1(
    superHeroName = "Spider Man", 
    superHeroPower = "superhuman agility and reflexes", 
     superHeroGroup = "The Avengers", 
     superHeroPrimaryVillain = "Green Goblin"
)
//Represents an instance of AdvancedSuperHero1 class above where
//the properties are declared in the constructor and initialized 
//in the class body.
val ironMan = AdvancedSuperHero2(
    heroName = "Iron Man",
    superPower = "Armored suit",
    superHeroGroup = "The Avengers",
    primaryVillain = "The Mandarin"
) 
//Represents an instance of AdvancedSuperHero2 class above where 
//we both declare and initialize the properties in the 
//constructor using the var or val keywords.

We can also access the properties and functions of the superhero object from above:

println(spidey.name) //Spider Man
println(spidey.primaryVillainName) //Green Goblin
println(spidey.fightCrime()) //Spider Man is using superhuman agility and reflexes to fight crime

If we try to access the properties of AdvancedSuperHero1 spidey object using the parameters supplied into its constructor, we get an exception because these are not properties of the class rather they are simply parameters passed into the constructor and are not accessible within the class body. This is exemplified below:

println(spidey.superHeroName) //Unresolved reference: superHeroName
println(spidey.superHeroPrimaryVillain) //Unresolved reference: superHeroPrimaryVillain
println(ironMan.heroName) //Iron Man
println(ironMan.fightCrime()) //Iron Man is using Armored suit to fight crime

Conclusion

We have looked at some of the basics of Kotlin class features in this post and how they are essential to developing object-oriented solutions. As we delve deeper into our Kotlin journey, we will cover additional specialized classes that are part of the ecosystem, like data classes, inline classes, enum classes, etc., and other ideas related to classes and objects, like constructors and access modifiers.