Building blocks of Kotlin modern development: An intro to classes and objects
In this article, we will go through the following key points:
Introduction
Key Definitions
Understanding the class keyword
Instantiating Objects
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.
Class
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.