Variables (val & var)

Reviewed & published by Brayan K

Kotlin is a modern, concise language that gives you two ways to name data: val for read-only values and var for ones you can change.

Part of the free Kotlin course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

By the end of this lesson you'll know exactly when to reach for each, how type inference saves you keystrokes, and how Kotlin's basic types — Int, Double, Boolean, Char, and String — work.

What You'll Learn in This Lesson

1️⃣ val vs var

Kotlin has exactly two keywords for declaring variables. val creates a read-only reference that can be assigned once. var creates a mutable reference you can reassign as many times as you like. The rule of thumb: prefer val, and switch to var only when you must.

fun main() {
    // val = read-only ("value"). You can NEVER reassign it.
    val name = "Ada"
    println(name)                 // -> Ada

    // var = mutable ("variable"). You CAN reassign it.
    var score = 10
    println(score)                // -> 10
    score = 25                    // reassigning a var is fine
    println(score)                // -> 25

    // Trying to reassign a val is a COMPILE error:
    // name = "Grace"  // ❌ Val cannot be reassigned

    // Note: val freezes the variable, not the object it points to.
    val items = mutableListOf("a", "b")
    items.add("c")                // ✅ allowed — the LIST changed, not the val
    println(items)                // -> [a, b, c]
}

The last two lines are the subtle part: val items can't point at a different list, but the list itself is mutable, so add still works. val freezes the reference, not the contents.

2️⃣ Types and Type Inference

Kotlin is statically typed — every variable has a type — but you rarely have to write it. The compiler infers the type from the value. When you do want to be explicit, add a colon and the type after the name. Kotlin's basic types include Int, Long, Double, Boolean, Char, and String.

fun main() {
    // Kotlin INFERS the type, but you can write it explicitly after a colon.
    val country: String = "Norway"   // text
    val population: Int = 5_400_000  // whole number (underscores aid reading)
    val area: Double = 385207.0      // decimal number
    val isNordic: Boolean = true     // true or false
    val initial: Char = 'N'          // a single character in 'single quotes'

    println("$country has $population people.")
    println("Area: $area km², Nordic: $isNordic, Initial: $initial")

    // Numbers have helpful conversion functions (no implicit widening):
    val asLong: Long = population.toLong()
    val asDouble: Double = population.toDouble()
    println("As Long: $asLong, as Double: $asDouble")
}

Notice Kotlin won't silently widen numbers: to treat an Int as a Double you call .toDouble(). This prevents accidental precision loss.

Your turn. Fill in the TODOs, following the 👉 hints, then run and compare.

// 🎯 YOUR TURN — replace each TODO then run main()
fun main() {
    // 1) A read-only variable for your favourite language
    val language = "TODO"      // 👉 use val, put text in quotes

    // 2) A mutable counter that starts at 0
    var clicks = 0             // 👉 this should be a var so we can change it

    // 3) Increase the counter twice
    clicks = clicks + 1
    clicks += 1                // shorthand for clicks = clicks + 1

    // 4) Print both values
    println("Language: $language, clicks: $clicks")

    // ✅ Expected (example):  Language: Kotlin, clicks: 2
}

Mini-Challenge: A Shopping Total

Use what you've learned about val, var, and types to build a small total. Write it yourself and check it against the example output.

// 🎯 MINI-CHALLENGE: a tiny shopping total.
// Save as Cart.kt and run main().
//
// 1. Make a val for an item name and a val for its price (Double).
// 2. Make a var quantity that starts at 1.
// 3. Increase quantity to 3 (reassign it).
// 4. Compute total = price * quantity and print a summary line.
//
// ✅ Example output:
//    3 x Notebook @ 4.5 = 13.5

fun main() {
    // your code here
}

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Variables

ConceptKotlin Syntax
Read-only variableval x = 5
Mutable variablevar y = 5
Explicit typeval n: Long = 5
Convert a numbern.toDouble()
Compound assigny += 1
Readable big number1_000_000

🎉 Lesson Complete!

Practice quiz

What does val declare in Kotlin?

  • A read-only reference you cannot reassign
  • A mutable variable
  • A function
  • A constant only known at runtime

Answer: A read-only reference you cannot reassign. val creates a read-only reference assigned once.

Which keyword lets you reassign a variable later?

  • val
  • var
  • const
  • let

Answer: var. var creates a mutable reference you can reassign.

What happens if you try to reassign a val?

  • It silently works
  • It compiles with a warning
  • A compile error: Val cannot be reassigned
  • It becomes a var

Answer: A compile error: Val cannot be reassigned. Reassigning a val is a compile-time error.

What does Kotlin's type inference let you do?

  • Skip declaring variables
  • Convert types automatically
  • Run without a compiler
  • Omit the type when it is clear from the value

Answer: Omit the type when it is clear from the value. val x = 5 infers Int, so you rarely write the type explicitly.

Does val make the object it points to immutable?

  • No, it only stops reassigning the reference; a mutable object can still change
  • Yes, everything becomes frozen
  • Only for numbers
  • Only inside functions

Answer: No, it only stops reassigning the reference; a mutable object can still change. val freezes the reference, not the contents; a mutable list can still be modified.

Which literal correctly declares a Char?

  • "N"
  • 'N'
  • Char(N)
  • `N`

Answer: 'N'. A Char uses single quotes, like 'N'; double quotes make a String.

How do you turn an Int into a Double in Kotlin?

  • It happens automatically
  • Cast with (Double)
  • Call .toDouble() explicitly
  • Add 0.0 silently

Answer: Call .toDouble() explicitly. Kotlin avoids implicit widening; call .toDouble() explicitly.

What is the value and type of val x = 5?

  • A String "5"
  • A Double 5.0
  • A function returning 5
  • An Int with value 5

Answer: An Int with value 5. An integer literal is inferred as Int.

What is idiomatic Kotlin's default choice between val and var?

  • Prefer val, use var only when reassignment is needed
  • Always use var
  • Always use const
  • It does not matter

Answer: Prefer val, use var only when reassignment is needed. Default to val to signal that the value will not change.

What do underscores do in a number like 1_000_000?

  • Cause a compile error
  • Are ignored but make big numbers readable
  • Multiply the value
  • Convert it to a String

Answer: Are ignored but make big numbers readable. Underscores are visual separators the compiler ignores.

Continue this course

Frequently asked questions

When should I use val instead of var?

Default to val for everything and only switch to var when you genuinely need to reassign the variable later. Using val communicates intent — it tells readers (and the compiler) that the value will not change — and prevents accidental reassignment bugs. Most idiomatic Kotlin code is heavily val-based; a codebase full of var is often a sign that something could be simplified.

Does val make the object immutable?

No — val only stops you from reassigning the variable to a different object. The object it points to can still change if it is mutable. For example, val list = mutableListOf(1) is read-only as a reference, but you can still call list.add(2). To make the contents immutable too, use a read-only type such as listOf(...) instead of mutableListOf(...).

Do I have to declare the type of every variable?

No. Kotlin has type inference, so val x = 5 automatically gives x the type Int and val name = "Ada" gives it String. You only need to write the type explicitly when the compiler cannot infer it (for example, a property with no initial value) or when you want to be more specific — such as declaring a Long or a nullable type. Many Kotlin developers omit types where they are obvious.

Why doesn't Kotlin convert numbers automatically like other languages?

Kotlin avoids silent, lossy conversions to prevent bugs. An Int will not automatically become a Long or a Double — you must call .toLong() or .toDouble() explicitly. This makes conversions visible in the code, so you never accidentally lose precision or truncate a value without realising it.