Skip to content
beginner Phase 1 · Kotlin Foundations

Kotlin Basics

Learn Kotlin syntax: variables, val vs var, types, string templates, and basic operators.

45m
3 problems
Topic Progress 0%

Val vs Var: Immutable and Mutable Variables

The Problem with Mutable State

Most bugs in software trace back to unexpected state changes. A function modifies a variable that another function depends on, and the system breaks in a way that is hard to reproduce. Kotlin addresses this at the language level by making immutability the default.

Val: Immutable Binding

A val declaration creates a read-only reference. Once assigned, you cannot reassign it. The underlying object may still be mutable (a list can have elements added), but the binding itself cannot change.

val name = "Kotlin"
// name = "Java"  // Compile error: Val cannot be reassigned

val numbers = mutableListOf(1, 2, 3)
numbers.add(4)       // Allowed: the list is mutable
// numbers = mutableListOf(5, 6)  // Compile error: Val cannot be reassigned

Var: Mutable Binding

A var declaration allows reassignment. Use it only when the value genuinely needs to change during execution, such as loop counters or accumulators.

var counter = 0
println(counter)  // 0
counter = 1
println(counter)  // 1

When to Use Which

Scenario Use Why
Configuration values val They should not change after initialization
Function parameters val (implicit) Parameters are always immutable in Kotlin
Loop counters var Must increment
Accumulator in a reduce var Must update during iteration
Any value that should not change val Prevents accidental mutation

The Kotlin compiler enforces this. If you declare a val and try to reassign it, you get a compile-time error. This eliminates an entire class of bugs before the code runs.

Type Inference

Kotlin infers types from the initializer, so explicit type annotations are usually unnecessary:

val age = 25          // Inferred as Int
val temperature = 36.6 // Inferred as Double
val active = true      // Inferred as Boolean

// Explicit type only when needed for clarity
val score: Int = 100

Shadowing

Kotlin allows variable shadowing within nested scopes, but it is discouraged because it reduces readability:

val x = 10
run {
    val x = 20  // Shadows outer x; compiler warning
    println(x)  // 20
}
println(x)  // 10

Basic Types and String Templates

Kotlin's Type System

Kotlin is statically typed, meaning every expression has a known type at compile time. However, type inference means you rarely need to write types explicitly. All types in Kotlin are objects, including primitives like Int and Boolean.

Number Types

val intVal: Int = 42
val longVal: Long = 42L
val floatVal: Float = 3.14f
val doubleVal: Double = 3.14
val byteVal: Byte = 127
val shortVal: Short = 32767

Kotlin does not distinguish between primitives and boxed types at the language level. The compiler optimizes to JVM primitives when possible, so there is no performance penalty for using Int instead of int.

Boolean and Char

val isActive: Boolean = true
val letter: Char = 'A'
val newline: Char = '\n'

String Templates

String templates eliminate string concatenation and make output readable. Prefix the string with """ for multi-line raw strings:

val name = "Amazon"
val year = 2026

// Simple variable reference
println("Company: $name")

// Expression in braces
println("Year: ${year + 1}")

// Multi-line raw string
val receipt = """
Company: $name
Year: $year
Total: ${100 * 1.08}
"""
println(receipt)

Without string templates, the same output requires concatenation:

// Java-style (avoid in Kotlin)
println("Company: " + name)
println("Year: " + (year + 1))

String templates work with any expression, not just variables:

val items = listOf("apples", "bananas")
println("Items: ${items.joinToString(", ")}")  // Items: apples, bananas
println("Count: ${items.size}")                   // Count: 2

Type Conversion

Kotlin does not perform implicit type widening. You must convert explicitly:

val intValue = 42
val doubleValue: Double = intValue.toDouble()  // Explicit conversion
val stringValue = intValue.toString()           // "42"

// This does NOT compile:
// val result: Double = intValue

Explicit conversion prevents silent precision loss. If you assign an Int to a Double without converting, the compiler rejects it, forcing you to acknowledge the conversion.

Operators in Kotlin

Arithmetic Operators

Kotlin supports the standard arithmetic operators with clear precedence rules:

val a = 10
val b = 3

println(a + b)   // 13 (addition)
println(a - b)   // 7  (subtraction)
println(a * b)   // 30 (multiplication)
println(a / b)   // 3  (integer division)
println(a % b)   // 1  (modulus)

Integer division truncates toward zero. To get a decimal result, convert one operand:

println(a.toDouble() / b)  // 3.3333...

Comparison Operators

println(5 > 3)    // true
println(5 < 3)    // false
println(5 >= 5)   // true
println(5 <= 4)   // false
println(5 == 5)   // true  (structural equality)
println(5 != 3)   // true  (structural inequality)

Kotlin distinguishes between structural equality (==) and referential equality (===). == calls equals() under the hood, so it compares content, not memory addresses.

Logical Operators

val x = true
val y = false

println(x && y)   // false (AND)
println(x || y)   // true  (OR)
println(!x)        // false (NOT)

Kotlin uses && and || which short-circuit: the right-hand side is not evaluated if the left-hand side determines the result.

Increment and Decrement

var count = 0
println(count++)  // Post-increment: prints 0, then count becomes 1
println(++count)  // Pre-increment: count becomes 2, then prints 2
println(count--)  // Post-decrement: prints 2, then count becomes 1
println(--count)  // Pre-decrement: count becomes 0, then prints 0

Infix Functions

Any function marked infix can be called with dot-call notation removed:

infix fun Int.times(str: String): String {
    return str.repeat(this)
}

println(3 times "ha")  // "hahaha"

This is not just syntax sugar. Infix functions make certain operations read like natural language, which is why Kotlin uses them extensively in its standard library (to, until, downTo, step).

Elvis Operator

The Elvis operator (?:) provides a default value when the left side is null. It is essential for null safety and covered in detail in the null safety topic:

val length = name?.length ?: 0

Range Operator

The .. operator creates ranges, which are iterated with for loops:

for (i in 1..5) {
    print("$i ")  // 1 2 3 4 5
}

// Downward range
for (i in 5 downTo 1) {
    print("$i ")  // 5 4 3 2 1
}

Practice Problems

0 / 3 solved
Temperature Converter

Write a Kotlin function that converts Celsius to Fahrenheit using the formula F = C * 9/5 + 32. Use val for the input and var for the result. Print the result with a string template.

Solution
fun convertCelsiusToFahrenheit(celsius: Double): Double {
    val fahrenheit = celsius * 9.0 / 5.0 + 32.0
    println("$celsius°C = ${"%.1f".format(fahrenheit)}°F")
    return fahrenheit
}
Val vs Var Compiler Check

The following code has a compile error. Identify which line causes the error and explain why. Fix it without changing the program's output.

Solution
fun main() {
    val name = "Kotlin"
    var year = 2027  // Changed to var since we reassign
    println("$name was created in $year")
}
// The error is on the line that reassigns 'year'.
// Since year needs to change, it must be declared with 'var'.
Expression Builder

Write a function that takes two numbers and an operator character ('+', '-', '*', '/') and returns the result as a Double. Handle division by zero by returning 0.0. Use string templates to log the operation.

Solution
fun calculate(a: Double, b: Double, op: Char): Double {
    val result = when (op) {
        '+' -> a + b
        '-' -> a - b
        '*' -> a * b
        '/' -> if (b != 0.0) a / b else 0.0
        else -> 0.0
    }
    println("$a $op $b = $result")
    return result
}

Quiz

1. What is the difference between val and var in Kotlin?

Question 1 options

2. What does the following code print? ```kotlin val x = 5 println("${x * 2}") ```

Question 2 options

3. Which statement about Kotlin type inference is correct?

Question 3 options

4. What is the result of `10 / 3` in Kotlin when both operands are Int?

Question 4 options

Flashcards

Question

What does val do in Kotlin?

Answer

Declares an immutable binding. Once assigned, the variable cannot be reassigned. The underlying object may still be mutable (e.g., a mutableListOf), but the binding is fixed.

Question

How do string templates work in Kotlin?

Answer

Prefix the string with $variableName for simple references, or ${expression} for complex expressions. Multi-line strings use triple quotes """. Templates are evaluated at runtime and interpolated into the string.

Question

What is the difference between == and === in Kotlin?

Answer

== checks structural equality (calls equals()), comparing content. === checks referential equality, comparing memory addresses. Use == for value comparison.

Question

Why does Kotlin require explicit type conversion?

Answer

Kotlin does not perform implicit widening (e.g., Int to Double). Explicit conversion prevents silent precision loss and makes type changes visible in the code.

Revision Notes

Key Takeaways

  • 1. Prefer val over var. Only use var when reassignment is genuinely required.
  • 2. Kotlin infers types at compile time, so explicit annotations are usually unnecessary.
  • 3. String templates replace concatenation and make output readable.
  • 4. Kotlin does not perform implicit type widening. Use explicit conversion methods.
  • 5. == calls equals() for content comparison; === checks referential identity.

Interview Tips

  • Explain why immutability matters: it prevents accidental state changes and makes code easier to reason about.
  • Be ready to describe the difference between val and var with concrete examples.
  • Know that Kotlin's type inference is compile-time, not runtime.
  • If asked about == vs ===, give an example where they differ (e.g., two new String objects with the same content).

Cheat Sheet

Kotlin Basics Cheat Sheet

Val vs Var:

  • val = immutable binding (preferred)
  • var = mutable binding (use only when needed)

Types:

  • Int, Long, Float, Double, Boolean, Char, String
  • All types are objects; compiler optimizes to primitives
  • No implicit widening: use .toDouble(), .toInt(), etc.

String Templates:

  • "Hello, $name" — simple variable
  • "${expression}" — complex expression
  • """...""" — multi-line raw string

Operators:

  • Arithmetic: +, -, *, /, %
  • Comparison: >, <, >=, <=, ==, !=
  • Logical: &&, ||, !
  • Range: .., downTo, until, step
  • Elvis: ?: (null default)