Start learning free

LESSON 10 OF 348 · VARIABLES

Data Types in Kotlin: Int, Double, String, Boolean, Char

Lesson Read the full explanation

TL ; DR:

In Kotlin, not all data is treated the same. There are different data types, such as:

  • int - represents integer numbers like 3 and -12

  • double - represents floating point numbers like 3.14

  • String - represents a string (sequence of characters) like "John Doe"

  • boolean - represents data that can have one of two values: true or false

  • char - represents a single character like 'a', 'Z' or '?'



Full lesson:

One instruction can have several distinct meanings depending on the data types it's working with.

For example, suppose you see this line of code somewhere in a program:

println(a + b)

Can you tell for sure what it does without knowing anything about the variables a and b?

It might add two numbers, if a and b are both numbers:

val a = 10
val b = -5

println(a + b) // Output: 5

But it also might concatenate two strings, if a and b are both strings:

val a = "Hello "
val b = "world!"

println(a + b) // Output: Hello world!

Of course, addition and string concatenation are two completely different operations and Kotlin needs to figure out which one to do.

And this is exactly why Kotlin assesses the data type of every value you use in your program. In order to be able to make these decisions when needed.

In our example, when it reads a + b, Kotlin checks the data type of a and b. If both are strings, it performs string concatenation. If both are numbers, it performs addition.


Invalid operations:

Asessing data types is also helpful for Kotlin to make sure that what you do with the data actually makes sense.

For example, computers can perform mathematical operations on numbers, but not on strings. Of course, nobody can stop you from writing a program like this:

val a = "peanut"
val b = "butter"

println(a * b) // TypeError

But when you try to run this program, Kotlin will throw a TypeError: bad operand types for binary operator '*'.

This is the way of Kotlin telling you: "Hey, I'm a little confused. How do you multiply two strings? I've never heard of that."


Nullability:

By default a Kotlin type can never hold null. To allow a missing value, add a ? to the type — String? means "a String, or null". To read a nullable value safely, use the safe call ?. (it yields null instead of crashing); use !! only when you are certain the value is non-null.

val name: String? = null // allowed because of the ?
println(name?.length)    // safe call, prints null
println(name!!.length)   // asserts non-null, crashes if null

Assignment
Follow the Coding Tutorial and let's practice with data types!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 4
    Ctrl + Enter to check

    KOTLIN · VARIABLES

    Data Types in Kotlin, explained

    What a data type is

    Every value in a Kotlin program has a type, which says what kind of data it is and what you can do with it. The compiler checks types before the program runs, so it can refuse operations that make no sense. These are the five types you meet first:

    TypeHoldsExamples
    Intwhole numbers3, -12
    Doubledecimal numbers3.14, -0.5
    Stringtext"John Doe"
    Booleantrue or falsetrue, false
    Charone character'a', 'Z', '?'

    Type names start with a capital letter. Int is right; int is not a Kotlin type. Strings use double quotes and a Char uses single quotes.

    Why the type changes what code does

    Look at this line, with no other context:

    println(a + b)
    

    What does it do? It depends on the types of a and b. With two numbers, + adds:

    val a = 10
    val b = -5
    
    println(a + b) // 5
    

    With two strings, + joins them:

    val a = "Hello "
    val b = "world!"
    
    println(a + b) // Hello world!
    

    Addition and concatenation are different operations. Kotlin picks the right one by looking at the types of the operands.

    Invalid operations are caught

    Types also stop you from doing nonsense. Multiplying two strings has no meaning:

    val a = "peanut"
    val b = "butter"
    
    println(a * b)
    

    Kotlin does not run this and fail halfway. It refuses to compile it:

    error: unresolved reference 'times' for operator '*'.
    

    The message means that String has no times function, which is what the * operator calls. The same checking applies to assignments: a variable declared as Int can't later be given a String or a decimal such as 5.5.

    Nullability

    By default, a type can never hold null, the marker for "no value". To allow it, add a question mark to the type: String? means "a String, or null".

    val name: String? = null
    println(name?.length)    // prints null
    println(name!!.length)   // throws a NullPointerException
    

    The safe call ?. gives null when the receiver is null instead of failing. The !! operator says "I am sure this is not null", and the program crashes if you are wrong. Writing plain name.length on a String? doesn't compile at all: the compiler reports that only safe or non-null asserted calls are allowed on a nullable receiver. So, in Kotlin, a missing value has to be dealt with in the code that uses it.

    The exercise

    The task builds four variables, one step at a time, each with an explicit type after the name:

    1. myInt of type Int, with a whole number.
    2. myFloat of type Double, with a decimal value. The name says float, but the type to use is Double, Kotlin's default for decimals. The separate Float type exists but needs an f suffix on its values.
    3. myString of type String, with text in double quotes.
    4. myBool of type Boolean, with true or false.

    Each variable is added below the previous ones, which stay in place:

    val myInt: Int = 5
    val myFloat: Double = 3.14
    val myString: String = "Hello"
    val myBool: Boolean = true
    

    The pattern is val name: Type = value. The value has to fit the type: val myInt: Int = 3.14 fails, and so does val myString: String = 5.

    Checking what type a value has

    Literals get their type from how they are written. 42 is an Int, 3.14 is a Double, "42" is a String, 'a' is a Char and true is a Boolean. A whole-number literal too big for an Int, such as 3000000000, is automatically a Long.

    You can ask a value at run time with is:

    val x: Any = 3.14
    println(x is Double)   // true
    println(x is Int)      // false
    

    Any is the type that accepts a value of any type, which makes it useful for this demonstration. In everyday code the compiler already knows the types, so you rarely need such a check.

    Details that surprise beginners

    • Integer division. 7 / 2 is 3, because both operands are Int. 7.0 / 2 is 3.5.
    • No silent widening. val d: Double = 5 doesn't compile, even though 5 fits in a double. Write 5.0, or convert with toDouble().
    • Range of Int. An Int has 32 bits, so the largest value is 2147483647, and adding 1 to it wraps around to -2147483648. The 64-bit Long type is for bigger numbers.
    • Inference. You may leave the type out, as in val myInt = 5, and Kotlin works it out. Writing it explicitly, as in this exercise, makes the intention visible.

    Key takeaways

    • Every value has a type: Int, Double, String, Boolean, Char, and more.
    • The type decides what operators such as + do and which operations are allowed.
    • Type mistakes are reported by the compiler before the program runs.
    • Types don't allow null unless you add ?.
    Keep going with KotlinAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free