Variables & Data Types

Kotlin · Lesson 4 of 29 7 min read Updated

Programs work with data: a student's marks, a train's departure time, the price of a phone. You store that data in variables, named boxes that hold a value. Every value in Kotlin also has a type, which decides what you can do with it.

In this lesson you will create variables with val and var, learn Kotlin's basic types, see how Kotlin works out types for you, and convert between types safely.

val and var

Kotlin has two keywords for variables:

  • val (from value) creates a read-only variable. Once it has a value, you cannot give it a new one.
  • var (from variable) creates a mutable variable that you can change later.
Kotlin
fun main() {
    val name = "Priya"
    var balance = 500
    println("$name has Rs $balance")

    balance = balance + 250
    println("$name has Rs $balance")

    // name = "Isha"   // does not compile: Val cannot be reassigned
}
Output
Priya has Rs 500
Priya has Rs 750

Prefer val. Use var only when the value really needs to change, such as a running total or a counter. Code with fewer changing parts is easier to read, and the compiler stops you from changing something by accident.

Name variables in camelCase: start with a lowercase letter and capitalise each new word, as in ticketPrice or totalMarks. Names cannot start with a digit or contain spaces.

Type inference

In the example above, you never wrote a type, yet name is a String and balance is an Int. Kotlin infers (works out) the type from the value. You can also write the type yourself after a colon:

Kotlin
val age: Int = 21
val city: String = "Jaipur"

Writing the type is useful when there is no value yet, or when you want a different type from the one Kotlin would choose. Either way, the type is fixed. A var that holds an Int can get a new Int, but never a String.

Basic types

TypeHoldsExample
Intwhole numbers, about plus or minus 2.1 billion42
Longvery large whole numbers100_000_000_000 or 42L
Short, Bytesmall whole numbers (rarely needed)val b: Byte = 10
Doubledecimal numbers, about 15-16 digits of precision99.5
Floatdecimal numbers, about 6-7 digits99.5f
Booleantrue or falsetrue
Charone character, in single quotes'A'
Stringtext, in double quotes"Kochi"

You can check what Kotlin inferred by printing a value's class name:

Kotlin
fun main() {
    val runs = 87
    val starsInGalaxy = 100_000_000_000
    val average = 45.5
    val ratio = 0.75f
    val passed = true
    val grade = 'A'
    val city = "Lucknow"

    println(runs::class.simpleName)
    println(starsInGalaxy::class.simpleName)
    println(average::class.simpleName)
    println(ratio::class.simpleName)
    println(passed::class.simpleName)
    println(grade::class.simpleName)
    println(city::class.simpleName)
}
Output
Int
Long
Double
Float
Boolean
Char
String

Some rules to remember:

  • A whole number is an Int if it fits; if it is too big for Int, it becomes a Long. Add L to force a Long: 42L.
  • A number with a decimal point is a Double. Add f for a Float.
  • Underscores make long numbers readable: 1_00_000 is one lakh, 1_000_000 is one million. The compiler ignores them.
  • Kotlin also has unsigned types such as UInt and ULong for values that are never negative (val count: UInt = 42u). You will rarely need them as a beginner.

Limits and overflow

Each number type has a smallest and largest value. If an Int calculation goes past the largest value, it overflows and wraps around to a large negative number, without any error.

Kotlin
fun main() {
    println(Int.MAX_VALUE)
    println(Long.MAX_VALUE)

    val max = Int.MAX_VALUE
    println(max + 1)
    println(max.toLong() + 1)
}
Output
2147483647
9223372036854775807
-2147483648
2147483648

Use Long for values that can grow very large, such as a country's population in a calculation, file sizes in bytes or amounts in paise.

Converting between types

Kotlin never converts number types silently. An Int is not automatically turned into a Long or Double, even though that would be safe. You call a conversion function instead: toInt(), toLong(), toDouble(), toString() and friends.

Kotlin
fun main() {
    val marks = 437
    val total = 500
    val percent = marks.toDouble() / total * 100
    println(percent)

    println(9.99.toInt())
    println((-9.99).toInt())

    val price = "1299".toInt()
    println(price + 1)
    println(price.toString() + "1")
}
Output
87.4
9
-9
1300
12991

Notice three things. First, marks.toDouble() turns the division into decimal division; without it, 437 / 500 would be whole-number division and give 0. Second, toInt() on a Double simply cuts off the decimal part, so 9.99 becomes 9, not 10. Third, "1299".toInt() turns text into a number, while toString() does the opposite, which is why the last line joins the texts "1299" and "1".

Writing val big: Long = marks does not compile, because marks is an Int. Write marks.toLong().

Constants and late initialisation

For a value that is known when the program is compiled and never changes, use const val at the top level of a file. By convention, constants use UPPER_SNAKE_CASE.

A val can also be declared first and given its value later, as long as it is assigned exactly once before use:

Kotlin
const val GST_RATE = 18

fun main() {
    val isMember = true
    val bill: Int
    if (isMember) {
        bill = 900
    } else {
        bill = 1000
    }
    println("Bill: Rs $bill, GST: $GST_RATE%")
}
Output
Bill: Rs 900, GST: 18%

The compiler checks that bill gets a value on every path before it is printed. You will see a shorter way to write this with an if expression in if & when Expressions.

Common mistakes

  • Using var everywhere. Start with val and change it to var only when the compiler tells you the value must change.
  • Expecting automatic conversion. val d: Double = 5 does not compile. Write 5.0 or 5.toDouble().
  • Whole-number division by accident. 7 / 2 is 3. Convert one side to Double to get 3.5.
  • Rounding with toInt(). It truncates. Use roundToInt() from kotlin.math (see Operators & Math) when you want rounding.
  • Ignoring overflow. Int silently wraps past 2,147,483,647. Use Long for big values.

Key points

  • val is read-only, var can change; prefer val.
  • Kotlin infers types from values, but each variable's type is fixed once chosen.
  • Main types: Int, Long, Double, Float, Boolean, Char, String.
  • Number types never convert silently; use toInt(), toLong(), toDouble() and toString().
  • Int overflows without an error; Long holds much bigger values.
  • const val declares compile-time constants at the top level.

Exercise

Rohan's monthly salary is Rs 38,500. Store it in a val, and store his savings rate of 15 percent in another val. Calculate his yearly savings as a Double and print it. Then store the yearly savings in a var, add a festival bonus of Rs 10,000 to it, and print the new value as a whole number using toInt().

Show solution
Kotlin
fun main() {
    val salary = 38_500
    val savingsRate = 15
    var yearlySavings = salary * 12 * savingsRate / 100.0
    println(yearlySavings)

    yearlySavings = yearlySavings + 10_000
    println(yearlySavings.toInt())
}
Output
69300.0
79300

Dividing by 100.0 makes the result a Double. yearlySavings is a var because it changes when the bonus is added.

Quick Quiz

1. What happens if you try to assign a new value to a val?

2. What type does Kotlin infer for val x = 3.0?

3. What does 9.99.toInt() return?

4. Which line compiles?

Learn Kotlin with a mentor

Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call