In the previous tutorial, you installed Kotlin and wrote your first programs. Now let’s learn about variables and types — the building blocks of every Kotlin program.

In this tutorial, you will learn:

  • The difference between val and var
  • All basic types in Kotlin
  • How type inference works
  • How to convert between types
  • How to use constants with const val

val vs var

Kotlin has two keywords for declaring variables:

  • val = value. Cannot be changed after assignment. Like final in Java.
  • var = variable. Can be changed.
// val — cannot change
val name = "Alex"
// name = "Sam"  // Error: Val cannot be reassigned

// var — can change
var age = 25
age = 26 // This is fine

Always use val by default. Only use var when you truly need to change the value. This is one of the most important rules in Kotlin. Using val makes your code safer and easier to understand.

val Does Not Mean Immutable

There is an important difference. val means the reference cannot change. But the object itself can still change.

val numbers = mutableListOf(1, 2, 3)
numbers.add(4)      // Fine — we change the list content
println(numbers)     // [1, 2, 3, 4]

// numbers = mutableListOf(5, 6, 7)  // Error: Val cannot be reassigned

Think of val like a label on a box. The label stays on the same box, but you can put new items inside the box.

Basic Types

Kotlin has these basic types:

Integer Types

TypeSizeRange
Byte8 bits-128 to 127
Short16 bits-32,768 to 32,767
Int32 bits-2.1 billion to 2.1 billion
Long64 bitsVery large numbers
val byte: Byte = 127
val short: Short = 32_767
val int: Int = 2_147_483_647
val long: Long = 9_223_372_036_854_775L  // L suffix for Long

Int is the default type for whole numbers. Use Long when you need bigger numbers.

You can use underscores in numbers for readability: 1_000_000 is the same as 1000000.

Floating Point Types

TypeSizePrecision
Float32 bits~6-7 decimal digits
Double64 bits~15-16 decimal digits
val float: Float = 3.14f      // f suffix for Float
val double: Double = 3.14159  // Default for decimal numbers

Double is the default for decimal numbers. Use Float only when you specifically need it (like in graphics code).

Boolean

val isKotlinFun: Boolean = true
val isJavaFun: Boolean = false

Boolean has only two values: true and false.

Char

val letter: Char = 'K'
val digit: Char = '5'
val symbol: Char = '@'

A Char is a single character. It uses single quotes ' '. Do not confuse it with String, which uses double quotes " ".

String

val greeting: String = "Hello, Kotlin!"

Strings are text. They use double quotes. We covered strings in detail in the previous tutorial.

Unit

fun printHello(): Unit {
    println("Hello")
}

// Unit is optional — these are the same:
fun printHello2() {
    println("Hello")
}

Unit means “no value”. It is like void in Java. If a function does not return anything, its return type is Unit. You don’t need to write it — Kotlin adds it automatically.

Type Inference

Type inference is one of Kotlin’s best features. The compiler figures out the type from the value you assign.

// You don't need to write the type
val name = "Sam"         // Kotlin knows: String
val age = 30             // Kotlin knows: Int
val height = 1.82        // Kotlin knows: Double
val isActive = true      // Kotlin knows: Boolean
val initial = 'S'        // Kotlin knows: Char

You can write the type explicitly. Both styles are valid:

// Explicit type
val country: String = "Germany"
val population: Long = 84_000_000L

// Inferred type
val country2 = "Germany"
val population2 = 84_000_000L

When should you write the type explicitly?

  • When the type is not obvious from the value
  • When you want a different type than the default (like Long instead of Int)
  • When there is no initial value
// You MUST write the type when declaring without an initial value
// This only works inside a function (local variables), not at top level
val name: String
name = "Alex"     // Assigned exactly once before first use

// At the top level or in a class, val MUST be initialized immediately:
val greeting: String = "Hello"

Number Literal Rules

val intLiteral = 42        // Int (default for whole numbers)
val longLiteral = 42L      // Long (L suffix)
val doubleLiteral = 42.0   // Double (default for decimals)
val floatLiteral = 42.0f   // Float (f suffix)

Summary:

  • No suffix = Int for whole numbers, Double for decimals
  • L suffix = Long
  • f suffix = Float

Type Conversions

Kotlin does not convert types automatically. This is different from Java.

val intNumber: Int = 42
// val longNumber: Long = intNumber  // Error: Type mismatch
val longNumber: Long = intNumber.toLong()  // Must convert explicitly

Here are all the conversion functions:

val intNumber = 42

val toLong = intNumber.toLong()       // 42L
val toDouble = intNumber.toDouble()   // 42.0
val toFloat = intNumber.toFloat()     // 42.0f
val toString = intNumber.toString()   // "42"
val toByte = intNumber.toByte()       // 42
val toShort = intNumber.toShort()     // 42

Double to Int Truncates

When you convert Double to Int, Kotlin truncates (cuts off the decimals). It does not round.

val pi = 3.99
val truncated = pi.toInt()
println(truncated) // 3, NOT 4

If you want to round, use kotlin.math.roundToInt():

import kotlin.math.roundToInt

val pi = 3.99
val rounded = pi.roundToInt()
println(rounded) // 4

String to Number

val str = "123"
val number = str.toInt()        // 123
val double = str.toDouble()     // 123.0

But what if the string is not a valid number?

val invalid = "hello"
// val number = invalid.toInt()  // Crash: NumberFormatException

// Safe conversion — returns null if invalid
val safe = invalid.toIntOrNull()
println(safe) // null

Always use toIntOrNull() (or toDoubleOrNull(), etc.) when you are not sure if the string is a valid number.

String Details

String Templates

String templates are the best way to put variables in strings:

val name = "Jordan"
val age = 28

// $ for simple variables
println("$name is $age years old")

// ${} for expressions
println("Next year, $name will be ${age + 1}")

String Equality

val a = "Kotlin"
val b = "Kotlin"

println(a == b)   // true — structural equality (compares content)
println(a === b)  // true — referential equality (same object)

In Kotlin, == compares content (like .equals() in Java). === compares if two variables point to the same object.

Useful String Functions

val text = "  Kotlin is great  "

text.trim()                    // "Kotlin is great"
text.trim().startsWith("Kotlin")  // true
text.trim().endsWith("great")     // true
text.trim().substring(0, 6)       // "Kotlin"
"one,two,three".split(",")        // [one, two, three]
"Kotlin".reversed()               // "niltoK"
"Ha".repeat(3)                    // "HaHaHa"
text.trim().uppercase()           // "KOTLIN IS GREAT"
text.trim().lowercase()           // "kotlin is great"

Raw Strings

Triple-quoted strings don’t need escape characters:

// Regular string — need to escape backslashes
val path1 = "C:\\Users\\Alex\\Documents"

// Raw string — no escaping needed
val path2 = """C:\Users\Alex\Documents"""

// Multi-line raw strings
val json = """
    {
        "name": "Alex",
        "age": 25
    }
""".trimIndent()

trimIndent() removes the common whitespace from the beginning of each line.

Constants: const val vs val

Kotlin has two ways to declare constants:

val — Runtime Constant

val currentTime = System.currentTimeMillis()  // Value computed at runtime

const val — Compile-Time Constant

const val MAX_USERS = 100
const val APP_NAME = "Kotlin Tutorial"
const val PI = 3.14159265

The rules for const val:

  1. Must be at the top level or in a companion object
  2. Must be a String or a primitive type (Int, Double, etc.)
  3. The value must be known at compile time (no function calls)
// Top level — outside any class
const val MAX_SIZE = 1024

class Config {
    companion object {
        // Inside a companion object
        const val VERSION = "1.0"
    }
}

// Usage
println(MAX_SIZE)       // 1024
println(Config.VERSION) // 1.0

Use const val for true constants that never change. Use val for values that are computed at runtime.

Summary

ConceptDescription
valImmutable reference — cannot reassign
varMutable reference — can reassign
Type inferenceCompiler figures out the type
IntDefault for whole numbers
DoubleDefault for decimal numbers
LongUse L suffix
FloatUse f suffix
toInt(), toDouble()Explicit type conversion
toIntOrNull()Safe conversion (returns null if invalid)
const valCompile-time constant

Source Code

You can find the complete source code for this tutorial on GitHub:

KT-3 Source Code on GitHub

What’s Next?

In the next tutorial, Kotlin Tutorial #4: Null Safety — Kotlin’s Best Feature, you will learn:

  • Why null causes so many bugs
  • How Kotlin prevents null problems at compile time
  • All null safety operators: ?, ?., ?:, !!
  • Safe casting with as?
  • lateinit for late initialization

Null safety is what makes Kotlin truly special. Don’t miss it.


This is part 3 of the Kotlin Tutorial series. Check out Part 2: Installing Kotlin if you missed it. Need a quick reference? See the Kotlin Cheat Sheet.