A Beginner’s Guide to Kotlin: Syntax, Basics, and Core Concepts

What is Kotlin

Kotlin Logo

Kotlin is an island in Russia… Wait… an island? Yes, that’s the joke: Java is an Indonesian island, and since Kotlin was created by Russians, Kotlin is a Russian island.

The enterprise behind Kotlin is JetBrains. You may know JetBrains for the famous IDEs they develop (like IntelliJ IDEA, RubyMine, and WebStorm).

Kotlin was created in 2011 and runs on the JVM (but there are many ports, today, Kotlin runs not only on the JVM but also on JavaScript, native binaries, and even WebAssembly, making it a truly multi-platform language).

Kotlin became widely adopted when Google announced it would be the default language for Android development back in 2019.

How to learn a new language?

If you want to learn something about tech, I recommend you start by reading the documentation. Just open the documentation and start reading. All content used in this article is available under the official language documentation, and all examples were created using the documentation as a reference.

How can I start?

Kotlin has an online playground on the official site. That means you can write your first code and test things without installing anything.

The Language

Hello World

Let’s start with the basic example we use to learn every language: let’s print Hello World.

fun main() {
    println("Hello world!")
}

Mutable vs. Immutable

In Kotlin, we have a concept of mutable and immutable types:

  • Mutable: Can be changed after creation. Kotlin provides mutable versions of many types when you need flexibility.
  • Immutable: Can’t be changed after it’s created. Kotlin encourages immutability because it helps prevent accidental changes, reduces bugs, and makes code easier to reason about, especially in concurrent environments.

A notable design choice in Kotlin is that most data structures provide both immutable and mutable versions, where the mutable type extends the immutable one through polymorphism. The immutable variant is the default, while mutability must be requested explicitly. This design offers a consistent API surface, encourages safer patterns, and reinforces immutability as the standard approach in the language.

A few examples can be found in the variables section (var and val) and in collections (lists, sets, and maps).

Variables

Variables are used to store values, as in any language, but in Kotlin, you need to declare a variable using var or val. But wait, what’s the difference?

  • val is a read-only variable.
  • var is a mutable variable.

Variable type can be declared by the developer, or the compiler can infer it from the value.

val intVariable: Int = 10
val strVariable = "Kotlin"

var willBeChangedLater = 1
willBeChangedLater = 2
println(willBeChangedLater)

Basic types

Kotlin has a concept of type inference, meaning some type information in the code may be omitted and inferred by the compiler.

Integers

Integers have Byte, Short, Int, and Long. Integers store whole numbers. The difference between the kinds is the memory allocated.

val intVariable: Int = 1

Floating-point numbers

For floating points, we have Float and Double. The difference is:

  • Double: for higher precision, such as in scientific, financial, and engineering calculations.
  • Float: when memory efficiency is a primary concern, and the reduced precision is acceptable.

To indicate a float value, use the letter f after the number.

val testVariable: Float = 0.5f
val totalValue: Double = 99.99

Booleans

The basic type in any language. Kotlin uses true` or `false.

val isEnabled = true
val isDisabled = false

Characters and Strings

Under the hood, a string is just a sequence of characters. A Char stores a single character, and a String stores multiple characters.

  • Chars must be single-quoted
  • Strings are double-quoted
val testChar: Char = 'A'
val testString: String = "ABC"

Collections

For collections, we have three kinds: lists, sets, and maps.

Lists

By concept, a list is an ordered collection of items.

We have two kinds of lists: the read-only list and the mutable list.

Read-only Lists

A read-only list is a list that doesn’t change.

val languagesToLearn = listOf("Kotlin", "Pascal", "Fortran")
println(languagesToLearn) // [Kotlin, Pascal, Fortran]

Mutable Lists

A mutable list is a list that can be changed.

val names: MutableList<String> = mutableListOf("Code", "Miner")
names.add("42")
println(names) // [Code, Miner, 42]

Sets

Sets are like lists, but unordered and only store unique items.

val readOnlySet = setOf("Code", "Miner", "42")
println(readOnlySet) // [Code, Miner, 42]

val setOfStrings: MutableSet<String> = mutableSetOf("Code", "Miner", "42")
println(setOfStrings) // [Code, Miner, 42]

Maps

Maps store items as key-value pairs. You access the value by referencing the key.

val readOnlyMap = mapOf("John" to 1, "Mark" to 10)
println(readOnlyMap) // {John=1, Mark=10}

val mapExample: MutableMap<String, Int> = mutableMapOf("John" to 1, "Mark" to 10)
println(mapExample) // {John=1, Mark=10}

Flow

If

Ifs are the basics in any language. The syntax is simple: the condition is inside parentheses, and curly braces are used to write the content.

val testNumber = 1

if (testNumber > 10) {
    println("More than ten tests")
} else {
    println("Not tested yet")
}

When

When is like a “switch-case”.

val number = 1

when (number) {
    1 -> println("One")
    2 -> println("Two")
    else -> println("Number")
}

For

The for loop iterates over items, like in most common languages:

for (counter in 1..10) {
    println(counter)
}

While

While loops execute as long as the condition inside the parentheses evaluates to true.

var testCounter = 1
while (testCounter < 5) {
    println("Test")
    testCounter++
}

Do-While

Do-while loops execute at least once and continue while the condition at the end evaluates to true.

do {
    println("Bake a cake")
    testCounter++
} while (testCounter < 5)

Classes

To declare a class, you just need to use the class keyword:

class Message

A class can contain properties and functions:

class Message(val text: String) {
    val helloPrefix = "Hello"

    fun sayHello() {
        println("$helloPrefix $text")
    }
}

fun main() {
    val instanceOfClass = Message("World")
    instanceOfClass.sayHello()
}

Null safety

What is null?

Null means the value is missing or not set yet.

To help prevent issues with null values in your programs, Kotlin has null safety in place. Null safety detects potential problems with null values at compile time, rather than at run time.

Nullable

By default, Kotlin doesn’t allow a value to accept null.

Null types are declared by using ? after the type declaration.

The safe-call operator (?.) returns null if either the object or one of its accessed properties is null.

fun main() {
    var neverNull: String = "This can't be null"
    // neverNull = null // Compiler error

    var nullable: String? = "You can keep a null here"
    nullable = null

    var inferredNonNull = "The compiler assumes non-nullable"
    // inferredNonNull = null // Compiler error

    fun strLength(notNull: String): Int {
        return notNull.length
    }

    println(strLength(neverNull))
    // println(strLength(nullable)) // Compiler error
}

Elvis operator

You can provide a default value to return if a null value is detected by using the Elvis operator ?:.

fun main() {
    val nullString: String? = null
    println(nullString?.length ?: 0)
}

Why Elvis?

If you rotate the ?: on its side, you will see Elvis Presley!

Elvis Operator

References

We want to work with you. Check out our Services page!