Getting Started

<< RETURN: Coding / Kotlin / Coding with Kotlin

Setting Up Environment

Getting started with coding in Kotlin is easy. You’ll need a tool to write and execute Kotlin code such as an Integrated Development Environment (IDE). There are many IDEs that one can use to type out Kotlin code. I’ll be using IntelliJ IDEA Community Edition as it is free and it so happens that it comes from JetBrains, the creators of Kotlin.

You can download IntelliJ IDEA with this link. Note that the Ultimate Edition has a 30-day trial period. You are free to choose whichever edition you want to use for coding.

After downloading, simply double click on the installer and follow the steps.

For installation options, feel free to check whichever you want. After successful installation you will also need to create a project before you can start coding.

Select New Project
Type in the Name & Select the Location of your project, and select Kotlin as the language for this project. Click on Create and voila.

For the rest of this section I’ll be running through some basic stuff that you’ll need to know or come across when using Kotlin.

Variables & Data Types

Variables

What are variables? From a high level view, variables are simply names that can be assigned values of different data types and they store the assigned value for future use.

Kotlin
fun main() {
  // the value 1 is stored in a
  val a = 1
  
  /*
  
    some code later...
  
  */
  
  // Console output -> 1
  println(a)
}

Note that the main() function, used in most of the sample code blocks, is the entry point of all Kotlin programs. Only code called directly or indirectly from main() will be executed. Kotlin code without the main() function can only be used as a package or library.

Data Types

The stored data can be of different types. Programmers will use different data types depending on their needs. Some basic data types are Integer, Float, Boolean and String.

Kotlin
val a = 1 // Integer
val b = 2.0 // Double
val c = true // Boolean
val d = "Hello World" // String

In Kotlin, variables are usually declared using a val keyword or the var keyword. Using val means that the particular variable cannot be reassigned, while var allows for reassignment.

Kotlin
fun main() {
  val a = 1 
  a = 2 // Error when compiling
  println(a)
}
Kotlin
fun main() {
  var a = 1
  a = 2
  println(a) // Console output -> 2
}

As seen above, a variable declared with the val keyword cannot be reassigned. However, let’s take for example Arrays.

Kotlin
fun main() {
  val myArray = mutableListOf<Int>()
  myArray.add(0)
  myArray.add(1)
  
  for (item in myArray) {
    println(item)
  }
  /*
    =Console output=
    0
    1
  */
}

In the sample code above, I declared myArray using the val keyword but I am still able to add values (0 and 1) to it afterwards. This is because, for objects and more complex data types this isn’t considered reassignment. Below is a similar sample code, but with an added reassignment which causes a compile time error.

Kotlin
fun main() {
  val myArray = mutableListOf<Int>()
  myArray.add(0)
  myArray.add(1)
  
  for (item in myArray) {
    println(item)
  }
  /*
    =Console output=
    0
    1
  */
  
  myArray = mutableListOf<Int>() // Compile Error
}
Static Typing

Kotlin is also statically typed. This means that every Kotlin variable needs to know its data type at compile time. If you want to declare a variable first and assign a value to it later on, you will need to explicitly declare its data type.

Kotlin
fun main() {
  // val a -> Compile Time Error
  val a: Int
  val b = 2
  
  a = b + 1
  
  println(a) // Console output -> 3
}

As seen above, I wanted to initialize the variable a later on. Thus, I had to declare it as type Int first. Otherwise, I would have encountered an error during compilation.

However, you won’t need to worry about this if your variable is initialized immediately. This is because Kotlin provides type inference.

Type Inference

Kotlin Compilers will automatically know what data type a particular variable is, based on the type of data assigned to it. This frees up the programmer from having to explicitly declare the data type of every single variable.

Kotlin
fun main() {
  val a = 1
  var b: Int = 2
  
  println(a+b) // Console output -> 3
}

As seen above, the variable a has no explicit data type definition, whereas variable b is defined explicitly as an Integer. Because of type inference, there is no need to explicitly define the data type of variable a and yet it is possible to add it with another Integer variable b.

This works not only for Integer variables, but for other data types as well.

Operators

We use operators to perform calculations and manipulate data.

Addition Operator +
Kotlin
fun main() {
  var a = 1
  val b = 2
  a += 1 // a = a + 1
  println(a+b) // Console output -> 4
}

For numerical data types, the + operator adds those values together.

Kotlin
fun main() {
  val a = "Hello"
  val b = " World"
  println(a+b) // Console output -> Hello World
  
  val f = mutableListOf<Int>(1, 2)
  val g = mutableListOf<Int>(3, 4)
  
  f += g
  
  for (item in f) {
      println(item)
  }
  /*
    =Console output=
    1
    2
    3
    4
  */
}

For strings and arrays, it concatenates them together.

Subtraction Operator -
Kotlin
fun main() {
  var a = 1
  val b = 2
  a -= 1 // a = a - 1
  println(a-b) // Console output -> -2
}

For numerical data types, the - operator subtracts the left hand side value with the right hand side value.

Kotlin
fun main() {
  val f = mutableListOf<Int>(1, 2, 3, 4, 2)
  val g = mutableListOf<Int>(2)
  
  f -= g.toSet()
  
  for (item in f) {
      println(item)
  }
  /*
    =Console output=
    1
    3
    4
  */
}

For arrays, f -= g basically means to take out everything in array f that exists in array g. Thus, all the 2s were removed.

Multiplication Operator *
Kotlin
fun main() {
  var a = 1
  val b = 2
  a *= 1 // a = a * 1
  println(a*b) // Console output -> 2
}

For numerical data types, the * operator multiply those values together.

Division Operator /
Kotlin
fun main() {
  var a = 1
  val b = 2
  a /= 1 // a = a / 1
  println(a/b) // Console output -> 0
}

For numerical data types, the / operator divides the right hand side value from the left hand side value. Also, for integer division in Kotlin, the result will be rounded to the nearest integer.

Kotlin
fun main() {
  var a = 1.0
  val b = 2.0
  a /= 1 // a = a / 1
  println(a/b) // Console output -> 0.5
}

If you want to keep the accuracy of your calculation, consider switching to using Float/Double.

Modulo Operator %
Kotlin
fun main() {
  var a = 5
  val b = 10
  a %= 3 // a = a % 3
  println(a%b) // Console output -> 2
}

For numerical data types, the % operator divides the right hand side value from the left hand side value and gets the remainder.

Conditional Branches & Control Flow

Now we move on to Conditional Branches and Control Flows. This section is mostly intuitive, so I won’t be explaining too much. Basically, if the specified conditions are met, then the code within that section is executed.

If Else
Kotlin
fun main() {
  val a = 4
  
  if (a > 5) {
      println("a > 5")
  } else if (a < 0) {
      println("a < 0")
  } else {
      println("0 < a < 5")
  }
  
  // Console output -> 0 < a < 5
}

In the above code, after failing the first two conditions, it goes into the else block. Note that if either the first or second condition succeeds, subsequent blocks would not be executed.

While
Kotlin
fun main() {
  var a = 0
  
  do {
      println("Happy")
      a += 1
  } while (a < 3)
  
  while (a < 5) {
      println("Birthday")
      a += 1
  }
  
  /*
    =Console output=
    Happy
    Happy
    Happy
    Birthday
    Birthday
  */
}

Both the while and do-while loop will execute repeatedly until the specified condition fails. Their main difference being that do-while will run first & check later, and while will check first & run later.

When
Kotlin
fun main() {
  val a = 2
  when (2) {
      1 -> println("when 1")
      2,3,4 -> {
          println("when 2, 3 or 4")
      }
      else -> {
          println("when not 1-4")
      }
  }
  
  // Console output -> when 2, 3 or 4
}

Similar to the if statement, but when is usually used when there are more conditions and the conditions are not complex (kind of like multiple choice).

For
Kotlin
fun main() {
  for (i in 0..5) {
      println(i)
  }
  /*
    =Console output=
    0
    1
    2
    3
    4
    5
  */
  
  for (i in 0..5 step 2) {
      println(i)
  }
  /*
    =Console output=
    0
    2
    4
  */
  
  for (i in 0 until 5) {
      println(i)
  }
  /*
    =Console output=
    0
    1
    2
    3
    4
  */
  
  val s = "Hello"
  
  for (i in s.indices) {
      println("[$i] = ${s[i]}")
  }
  /*
    =Console output=
    [0] = H
    [1] = e
    [2] = l
    [3] = l
    [4] = o
  */
  
  for (i in s.indices step 2) {
      println("[$i] = ${s[i]}")
  }
  /*
    =Console output=
    [0] = H
    [2] = l
    [4] = o
    [6] = W
    [8] = r
    [10] = d
  */
  
  for (c in s) {
      println(c)
  }
  /*
    =Console output=
    H
    e
    l
    l
    o
  */
}

for loops are really powerful. You can specify what values you want to iterate through as well as how big each increment will be. This is even more so when dealing with strings and arrays.

Functions

Another key component of any programming language are functions. They are extremely useful and most good coding practices (DRY) likely involve functions.

Kotlin
fun main() {
  var a = 10
  var b = 5
  
  while (a > 0) {
    println("Countdown: $a")
    a -= 1
  }
  
  while (b > 0) {
    println("Countdown: $b")
    b -= 1
  }
}

Take for example the above code, it will output 10 to 1 and then 5 to 1. But using functions, we can make the code simpler and also reusable in other parts of the code.

Kotlin
fun main() { 
  countDown(10)
  countDown(5)
}


fun countDown(i: Int) {
  var num = i
  while (num > 0) {
    println("Countdown: $i")
    num -= 1
  }
}

Now the main() function is much cleaner and readable. Also, the function countDown(Int) can now be called from other parts of the program to perform the countdown tasks.

Kotlin
fun main() {
    sayGreetings()

    val decrementNum = { i: Int ->
        var num = i
        while (num >= 0) {
            println("Countdown: $num")
            num -= 1
        }
        0 // Kotlin treats this as the return value
    }

    var count = 5

    while (count >= 0) {
        if (isPerfectMoment(count)) {
            sayHappyBirthday()
            break
        } else {
            count = decrementNum(count)
        }
    }
}

fun sayGreetings() {
    println("Hello!")
}

fun sayHappyBirthday() {
    println("Happy Birthday!")
}

fun isPerfectMoment(count: Int): Boolean {
    return count == 0
}

In the above example, there are different kind of functions. Functions that perform some tasks but don’t return anything like sayGreetings() and sayHappyBirthday(), functions that take in an argument and return a value like isPerfectMoment(Int): Boolean, and anonymous functions like decrementNum(Int): Int that can only be used within the same scope.

This section simply aims to bring to your attention that there are different ways to declare and use functions, details will be discussed in other posts.

Objects & Classes

What are Classes and Objects? Classes are like blueprints, and Objects are the things created from that blueprint.

Kotlin
fun main() {
    val human = Animal(2, 80.5, "Human")
    val tiger = Animal(4, 105.2, "Tiger")
    val whale = Animal(0, 130000.0, "Whale")

    human.sayIntro()
    tiger.sayIntro()
    whale.sayIntro()
}

class Animal(private val legs: Int, private val weight: Double, private val name: String) {

    private fun getLegs(): Int {
        return legs
    }

    private fun getWeight(): Double {
        return weight
    }

    private fun getName(): String {
        return name
    }

    fun sayIntro() {
        println("Hello, I am a ${getName()}. I weigh ${getWeight()} kg and I have ${getLegs()} legs.")
    }

}

/* 
  =Console output=
  Hello, I am a Human. I weigh 80.5 kg and I have 2 legs.
  Hello, I am a Tiger. I weigh 105.2 kg and I have 4 legs.
  Hello, I am a Whale. I weigh 130000.0 kg and I have 0 legs.
*/

As seen in the above sample code, with the Animal class we can instantiate as many animal objects as we want. Being able to store and associate multiple values to a single variable is extremely powerful. This also makes code more maintainable, scalable, and modular.

Next Up

Once you’re done getting your feet wet with basic programming in Kotlin and IntelliJ IDEA, you can see how to get started with Compose Multiplatform