Classes are perhaps the most defining feature of Kotlin, allowing us to create custom data types with specific data and behaviour. This feature of languages is what sets up the category of programming languages called Object-Oriented Programming languages which include some of the most popular languages today like Java, Ruby, Python, C++, Swift, C#, Rust etc.
Quite simply, with classes you create a container where you can define specific variables that stores information and create specfic functions that can create custom behavior.
Data Container
The first thing we can understand about classes as a grouping of data under a common name.
In this case we have made an RPG like class called Hero
which has three properties
name
of the Hero which is aString
since players can use any number of characters to define their nametype
of the Hero that is game specific roles that could have different custom behaviour likeMage
orWarrior
healthPoints
of the Hero which is anInt
which represents how alive the character is and having it go too low can end the game.
In each case you have to consider how to use classes based on your specific code design and what can be composed of what. There are often many different ways to do it so don’t worry if you see lots of the same approaches to different problems.
Basic class syntax
So all we’re doing is creating a more complex data type out of existing data types we know. For this we use the class
keyword and curly {}
brackets all the variables it holds. Here I give some default values for each of the three variables.
class Hero {
var name: String = "Harsh"
var type: String = "Mage"
var healthPoints: Int = 120
}
Here I setup the Hero
class with the three variables and sample values. Now when I want to use this new data type in my code is declare it like any other variable. Every time you make a variable that is of a particular type, we call it an object
of that class. Below is an object of the type Hero
that we refer to as harsh
in the code.
val harsh: Hero = Hero()
// Getting
println(harsh.type)
// => Mage
println(harsh.healthPoints)
// => 120
// Setting
harsh.healthPoints = 70
println(harsh.healthPoints)
// => 70
Using variable_name.property
can let me access the inner variables that make up the class. You can print it, use it in some computation and use the =
as if you’d change any other variable.
Constructor
Let’s make another object and then set the fields we want.
val varshini = Hero()
varshini.type = "Warrior"
varshini.name = "Varshini"
Now you start to see how this can quickly get annoying. Every time you make a new object
you have to setup each field with .variableName =
. For large classes this can take a lot of space and this adds clutter. This also results in making everything var
that we want custom but it will be better if we can set some properties to be assignable once.
For this we can use constructors. Constructors are like special functions that are run when an object is first created. All you have to do is add a list of parameters in ()
round brackets and then use it to set up your variables. Make sure they’re called something different than the variables in your class. Thanks to constructors we were able to make name
and type
into val
which we were not able to do earlier.
class Hero(setName: String, setType: String) {
val name: String = setName
val type = setType
var healthPoints = 120
}
And declaring a new object becomes nicer too
val varshini = Hero("Varshini", "Warrior")
Creating an object with name "Varshini"
, type "Warrior"
and healthpoints 120
a default value.
Constructor short hand
Even that sytnax can get quite repetitive. That’s why kotlin has made a simple shorthand for the most common case with constructors. If you add var
or val
before the variables it’ll create the same constructor for you automatically. Using =
we can set default values as well which is most common reason we often have many constructors too.
class Hero(val name: String, val type: String, var healthPoints: Int = 120)
And the earlier declaration code works unchanged.
val varshini = Hero("Varshini", "Warrior")
Adding functions
Inside the class body {}
you can add functions that are only avaible to objects that belong to the class. This is how we create specialized behavior for classes. Functions inside a class can access that object’s variables just by mentioning it by name.
class Hero(val name: String, val type: String, var healthPoints: Int = 120) {
fun attacked(damage: Int) {
healthPoints -= damage
println("Oh no! $name got attacked with $damage with $healthPoints hp left")
}
fun healed(hp: Int) {
healthPoints += hp
println("Yay! $name got healed with $hp with $healthPoints hp now")
}
}
As you can see above that the syntax for functions is the same but we can access properties of the object as well. To use these functions we use the same .
dot notation like object.functionName(...)
val varshini = Hero("Varshini", "Warrior")
varshini.attacked(50) // Oh no
// => Oh no! Varshini got attacked with 50 with 70 hp left
varshini.healed(70) // yay
// => Yay! Varshini got healed with 70 with 140 hp now
Calling functions on other objects
Our functions can also work on other objects of the same kind. This is useful because we want objects to interact with each other. Here we can set up functions that take Hero
objects and call actions on them.
class Hero(val name: String, val type: String, var healthPoints: Int = 120) {
fun attacked(damage: Int) {
healthPoints -= damage
println("Oh no! $name got attacked with $damage with $healthPoints hp left")
}
fun healed(hp: Int) {
healthPoints += hp
println("Yay! $name got healed with $hp with $healthPoints hp now")
}
fun attack(anotherPlayer: Hero) {
println("$name attacks ${anotherPlayer.name}")
val damage = if (type == "Warrior") 50 else 30
anotherPlayer.attacked(damage)
}
fun heal(anotherPlayer: Hero) {
println("$name casts heal on ${anotherPlayer.name}")
val hp = if (type == "Mage") 40 else 10
anotherPlayer.healed(hp)
}
}
Look at the two new functions, attack
and heal
, they both act differently based on the type
of the Hero
and use the .
dot notation to call things from other classes.
fun main() {
val harsh: Hero = Hero("Harsh", "Mage", 100)
val varshini = Hero("Varshini", "Warrior")
varshini.attack(harsh)
println()
println("Ooops, let me heal you")
varshini.heal(harsh)
println()
println("You aren't a mage so it wasn't that useful")
println("I'll do it myself")
harsh.heal(harsh)
}
which outputs
Varshini attacks Harsh
Oh no! Harsh got attacked with 50 with 50 hp left
Ooops, let me heal you
Varshini casts heal on Harsh
Yay! Harsh got healed with 10 with 60 hp now
You aren't a mage so it wasn't that useful
I'll do it myself
Harsh casts heal on Harsh
Yay! Harsh got healed with 40 with 100 hp now
Comments