In Unit 2, you set up your Kotlin development environment. Thanks to Unit 3, you now have an idea about object-oriented and function-oriented programming concepts.

Now you’re ready to dive into the Kotlin language.

In this unit, you’ll learn about statements versus expressions and look at all of the primitive data types the Kotlin language has to offer, with examples.

Unit objectives

In this unit, you will learn to:

  • Understand the difference between a statement and an expression
  • Understand Kotlin primitive data types

Working with Kotlin and the code for this unit

The Kotlin language specification is the ultimate source of information for its syntax, should you find it necessary to consult the source.

There are examples in this unit that you can work with yourself.

Make sure you’ve cloned the code from the learning path GitHub Repository to your computer.

First, open IntelliJ IDEA and create a new project for the code in this unit. (Note: You learned how in Unit 2.) Once you’ve created a project in IntelliJ, you can run the code in the IDE and debug it too, as I’ll demonstrate in the video that accompanies this unit.

Expressions and statements

In Kotlin, a statement is a line (or block) of code that performs some action but does not return a value.

For example, var a = 6 is a statement. It does not return a value, it simply declares a variable called a and assigns it a value of 6.

An expression always returns a value and provides a value for another expression (and that expression provides a value for yet another expression, and so on).

You see this in action when you run the REPL (Read-Eval-Print-Loop): Each time you enter an expression, the REPL evaluates the expression and echoes the result to the terminal. On the other hand, statements echo nothing.

Open a Terminal window and type kotlinc and press Enter to start the REPL. You’ll see the REPL Welcome message and a >>> prompt. Now type the following lines:

2 + 2
var a = 6
a
a + 2

You will see this output:

$ kotlinc
Welcome to Kotlin version 1.2.61 (JRE 1.8.0_181-b13)
Type :help for help, :quit for quit
>>> 2 + 2
4
>>> var a = 6
>>> a
6
>>> a + 2
8
>>>

Notice that the value of an expression is echoed to the console (for example, 2 + 2 evaluates to 4, which is echoed to the console). Statements, on the other hand, do not echo values (for example, var a = 6).

Another thought: Statements can contain expressions, but expressions cannot contain statements. If you’re familiar with the Java language, you may also be familiar with this typo (that is also a bug):

// Java
if (a = 6) {
    /* do something  when a is equal to 6*/
}

The if statement doesn’t actually compare the value of a to 6 because the developer meant to use == instead of =. Instead, the expression in parentheses assigns a a value of 6 instead. Since the assignment always returns true, the block executes regardless of what a‘s value actually was.

This particular bug is impossible to code in the Kotlin language because if is an expression, not a statement. Type this into the REPL and see for yourself:

if (a = 6) println(a)

You should get an error from the Kotlin compiler:

>>> if (a = 6) println(a)
error: assignments are not expressions, and only expressions are allowed in this context
if (a = 6) println(a)
    ^

>>>

Kotlin primitive data types (with literals examples for each)

Every Kotlin type is an object, including Kotlin’s primitive types. Under the hood, at the level of the JVM, it’s a different story. That’s the beauty of it, though: It is literally under the hood, so you don’t have to worry about it.

Kotlin provides eight primitive data types to work with:

  • Numeric types
    • Integer types include
      • Byte
      • Short
      • Int
      • Long
    • Floating point types include
      • Float
      • Double
  • Character type
  • Boolean type

You’ll work with all of these objects in this section.

Table 1 summarizes Kotlin’s primitive numeric data types:

Type Width (bits) Min Value Max Value Runtime Java type
Byte 8 -128 127 byte
Short 16 -32768 32767 short
Int 32 -2147483648 2147483647 int
Long 64 -9223372036854775808 9223372036854775807 long
Float 32 1.4E-45 3.4028235E38 float
Double 64 4.9E-324 1.7976931348623157E308 double

Table 1. Kotlin’s numeric primitive types and their corresponding Java built-in types

Kotlin also has a primitive type to work with characters and with booleans, respectively: Char and Boolean.

Numeric literals

A numeric literal is an object too, so you use particular formats to specify a numeric literal value depending on the type you want. These formats are summarized in Table 2, along with the types from Table 1 that correspond to them.

Literal Value Type
100 Int
100L Long
1.23f Float
1.23 Double

Table 2. Specifying different Kotlin numeric literal values and their corresponding types

There are other ways to specify a numeric literal value. Let’s say the number in decimal (base-10) format is 1234567890:

  • You can use hexadecimal notation to specify this value: 0x499602D2
  • You can also use binary notation: 0b01001001100101100000001011010010

Kotlin does not support octal literals.

In all formats (decimal, hexadecimal, or binary), you can use the underscore character to make the literal more readable. You can use underscores to separate large decimal numbers into constituent thousands and hexadecimal and binary numbers can be separated on byte boundaries:

val longThousands: Long = 1_234_567_890
val hexBytes: Long = 0x4996_02D2
val binaryBytes: Long = 0b01001001_10010110_00000010_11010010

Integer types

You work with whole numbers using one of Kotlin’s primitive integer types, depending on the size of the number you need (see Table 1).

Byte

The Byte primitive holds signed numeric values that fit in a single byte (8 bits). ByteExample.kt in Listing 1 shows how to create and work with Byte objects:

 01 fun main(args: Array<String>) {
 02     runByteExample()
 03 }
 04
 05 fun runByteExample() {
 06     // Declare Byte variables
 07     val byteMin: Byte = Byte.MIN_VALUE
 08     val byteMax: Byte = Byte.MAX_VALUE
 09     println("The value of byteMin is: $byteMin")
 10     println("The value of byteMax is: $byteMax")
 11
 12     // Declare Byte variable and initialize to a literal value
 13     var byte : Byte = 100
 14     println("The value of byte(100) is: $byte")
 15
 16     byte = 0x64
 17     println("The value of byte(0x64) is: $byte")
 18
 19     byte = 0b01100100
 20     println("The value of byte(0b01100100) is: $byte")
 21 }

Listing 1. ByteExample.kt shows how to work with Kotlin’s Byte primitive type

This example consists of two Kotlin functions:

  • runByteExample() (line 5) which contains the various usages of the Byte primitive
  • A main function (line 1) that you can use to run the code

You’ll learn more about functions in Units 5 and 7.

There is a lot of information on this topic, so let me explain enough that you won’t feel lost.

Lines 7 and 8 declare immutable variables that hold the minimum and maximum values of the Byte primitive type, respectively. Once assigned a value, an immutable variable cannot be assigned a new value.

Line 13 contains a mutable variable declaration to hold a Byte value. Mutable variables can be changed, as you see happen on lines 16 and 19.

The values are printed to the console using the println built-in function (lines 9, 10, 14, 17, and 20).

All of the examples in this unit follow this pattern. Going forward, I’ll skip the line numbers and the explanation to avoid repetition.

Start IntelliJ and open ByteExample.kt. You should see a window like Figure 1.

ByteExample.kt in IntelliJ IDEA

Figure 1. Opening ByteExample.kt in IntelliJ

Click Run (in the line number gutter next to the main function, Figure 1) and choose Run ByteExampleKt. The output pane appears under the editor pane with the output:

Output from running ByteExample.kt

Figure 2. Running ByteExample.kt and the output pane

Run all of the examples in this unit this way:

  • Open the file
  • Click Run
  • Choose the option to run that file

Short

The Short primitive holds numeric values that fit in two bytes (16 bits). Listing 2 shows how to work with Short:

01 /**
02  * The main function that drives the program
03  */
04 fun main(args: Array<String>) {
05     runShortExample()
06 }
07
08 /**
09  * Run the example to demonstrate
10  */
11 fun runShortExample() {
12     // Declare Short variables
13     val shortMin: Short = Short.MIN_VALUE
14     val shortMax: Short = Short.MAX_VALUE
15     println("The value of shortMin is: $shortMin")
16     println("The value of shortMax is: $shortMax")
17
18     // Declare Short variable and initialize to a literal value
19     var short: Short = 1000
20     println("The value of short(1000) is: $short")
21
22     short = 1_000
23     println("The value of short(1_000) is: $short")
24
25     short = 0x3e8
26     println("The value of short(0x3e8) is: $short")
27
28     short = 0b001111101000
29     println("The value of short(0b001111101000) is: $short")
30 }

Listing 2. ShortExample.kt shows you how to work with Kotlin’s Short primitive type

Open ShortExample.kt and run it to see the output. It will look very similar to Figure 2, the ByteExample.kt example.

Exercise 1

Using the line numbers in Listing 2, modify line 19 (the first assignment of the short variable) so that it looks like this:

    var short: Short = 50000

Why doesn’t the line containing the variable declaration compile?

Check your answer

The line returns this message and doesn’t compile:


Error:(19, 24) Kotlin: The integer literal does not conform to the expected type Short.

This is because the value (50000) is greater than the maximum value allowed for a Short (16 bits, which can hold 32767; see Table 1).


Int

The Int primitive holds numeric values that fit in four bytes (32 bits). Listing 3 shows how to work with Int:

/**
 * The main function that drives the program
 */
fun main(args: Array<String>) {
    intExamples()
}

/**
 * Run the example to demonstrate
 */
fun intExamples() {
    // Declare Int variables
    println(Int.MIN_VALUE)
    println(Int.MAX_VALUE)

    // Declare Int variable and initialize to a literal value
    var int: Int = 1000000
    println("The value of int(1000000) is: $int")

    int = 1_000_000
    println("The value of int(1_000_000) is: $int")

    int = 0xF4240
    println("The value of int(0xF4240) is: $int")

    int = 0b11110100001001000000
    println("The value of int(0b11110100001001000000) is: $int")
}

Listing 3. IntExample.kt shows you how to work with Kotlin’s Int primitive type

Run IntExample.kt in your IDE and see the output for yourself. Play around with the value assignments then run the program again and watch the output change.

Long

The Long primitive holds numeric values that fit in eight bytes (64 bits). Listing 4 shows how to work with Long:

/**
 * The main function that drives the program
 */
fun main(args: Array<String>) {
    runLongExample()
}

/**
 * Run the example to demonstrate
 */
fun runLongExample() {
    // Declare Long variables
    val longMin: Long = Long.MIN_VALUE
    val longMax: Long = Long.MAX_VALUE
    println("The value of longMin is: $longMin")
    println("The value of longMax is: $longMin")

    // Declare Long variable and initialize to a literal value
    var long: Long = 1000000000000L
    println("The value of long(1000000000000L) is: $long")

    long = 1_000_000_000_000L
    println("The value of long(1_000_000_000_000L) is: $long")

    long = 0xE8D4A51000
    println("The value of long(0xE8D4A51000) is: $long")

    long = 0xE8_D4_A5_10_00
    println("The value of long(0xE8_D4_A5_10_00) is: $long")

    long = 0b1110_1000_1101_0100_1010_0101_0001_0000_0000_0000
    println("The value of long(0b1110_1000_1101_0100_1010_0101_0001_0000_0000_0000) is: $long")

}

Listing 4. LongExample.kt shows you how to work with Kotlin’s Long primitive type

Run LongExample.kt in your IDE and see the output for yourself. Play around with the value assignments then run the program again and watch the output change.

Exercise 2

Change the value of one of the long assignments to make the value larger. How large can you make it before you get a compiler error? Why?

Check your answer


The Long type can hold a value up to 9223372036854775807 (Table 1). After that you will get a compiler error message like:


Error:(22, 12) Kotlin: The value is out of range

The reason is that any value higher than 9223372036854775807 (or less than -9223372036854775808) will not fit in a 64-bit signed integer.


Floating point types

You will use floating point types to store numbers with high-precision. Floating point numbers are governed by the IEEE 754 standard:

  • The Float primitive type holds a single-precision floating point number whose range is specified in Table 1
  • The Double primitive type holds a double-precision floating point number whose range is specified in Table 1

You can negate both floating point types (that is, make negative if positive and vice versa) by pre-pending a minus sign (-) to the value with no loss in precision, like this:

var float: Float = 1.23
var negativeFloat = -float

For a detailed discussion of floating point types, check out the Java Language Specification.

Float

The Float primitive holds single-precision floating point numbers that fit in four bytes (32 bits). Listing 5 shows how to work with Float:

/**
 * The main function that drives the program
 */
fun main(args: Array<String>) {
    floatExamples()
}

/**
 * Run the example to demonstrate
 */
fun floatExamples() {
    // Declare Float variables
    var floatMin: Float = Float.MIN_VALUE
    var floatMax: Float = Float.MAX_VALUE
    println("The value of floatMin is: $floatMin")
    println("The value of floatMax is $floatMax")
    println("The value of floatMax is $floatMax")

    // Declare Float variable and initialize to a literal value
    var float: Float = 1000000000.0f
    println("The value of float(1000000000.0f) is: $float")

    float = 1_000_000_000.0f
    println("The value of float(1_000_000_000.0f) is: $float")

    // Negate, with no loss in precision
    float = -Float.MAX_VALUE;
    println("The value of float(-Float.MAX_VALUE) is: $float")
}

Listing 5. FloatExample.kt shows you how to work with Kotlin’s Float primitive type

Run FloatExample.kt in your IDE and see the output for yourself. Play around with the value assignments then run the program again and watch the output change.

Double

The Double primitive holds double-precision floating point numbers that fit in eight bytes (64 bits). Listing 6 shows how to work with Double:

/**
 * The main function that drives the program
 */
fun main(args: Array<String>) {
    doubleExamples()
}

/**
 * Run the example to demonstrate
 */
fun doubleExamples() {
    // Declare Double variables
    val doubleMin = Double.MIN_VALUE
    val doubleMax = Double.MAX_VALUE
    println("The value of doubleMin is: $doubleMin")
    println("The value of doubleMax is: $doubleMax")

    // Declare Double variable and initialize to a literal value
    var double: Double = 1000000000000.0001
    println("The value of double(1000000000000.0001) is: $double")

    double = 1.0e22
    println("The value of double(1.0e22) is: $double")

    // Negate, with no loss in precision
    double = -Double.MAX_VALUE;
    println("The value of double(-Double.MAX_VALUE) is: $double")

}

Listing 6. DoubleExample.kt shows you how to work with Kotlin’s Double primitive type

Run DoubleExample.kt in your IDE and see the output for yourself. Play around with the value assignments then run the program again and watch the output change.

Character type

You work with characters using the Char primitive type, which is 16-bits wide and whose values range from 0 – 65535, inclusive. Characters in Kotlin are not numbers and you cannot treat them as such (as you can in Java, for example).

The range of characters you can use with Char allows you to engage the Unicode character set, which extends the ASCII character set to cover printable characters from languages around the world (in addition to English, for which ASCII was designed), as well as mathematical symbols and other symbols.

Listing 7 shows you how to work with Char:

/**
 * The main function that drives the program
 */
fun main(args: Array<String>) {
    charExamples()
}

/**
 * Run the example to demonstrate
 */
fun charExamples() {
    // Declare Char variable and initialize to a literal value
    var char: Char = 'a'
    println("The value of char('a') is: $char")

    char = '\"'
    println("The value of char('\"') is: $char")

    char = '\n'
    println("The value of char('\\n') is: $char")

    char = 'Z'
    println("The value of char('Z') is: $char")

    // Unicode symbols!
    char = '\u23f8'
    println("The value of char('\\u23f8') is: $char")

    char = '\u222b'
    println("The value of char('\\u222b') is: $char")

    char = '\u2654'
    println("The value of char('\\u2654') is: $char")
}

Listing 7. CharExample.kt shows you how to work with Kotlin’s Char primitive type

Run CharExample.kt in your IDE and see the output for yourself. Play around with the value assignments then run the program again and watch the output change. Make sure to add other unicode characters as well!

Exercise 3

Set one of the char variable assignments to the numeric literal 10. Explain what happens.

Check your answer

The Char type can only hold characters. When you try to assign a variable of type Char to the integer value 10, you get this compile error:


Error:(36, 12) Kotlin: The integer literal does not conform to the expected type Char

The reason is that 10 is an integer value and the Char type can only hold character values (that is, values from '\u0000' to '\uffff').


Boolean type

The Boolean primitive type can hold two values: true and false. Listing 8 shows how to work with Boolean:

/**
 * The main function that drives the program
 */
fun main(args: Array<String>) {
    booleanExamples()
}

/**
 * Run the example to demonstrate
 */
fun booleanExamples() {
    // Declare Boolean variables
    val booleanTrue: Boolean = true
    println("The value of booleanTrue is: $booleanTrue")

    val booleanFalse: Boolean = false
    println("The value of booleanFalse is: $booleanFalse")
}

Listing 8. BooleanExample.kt shows you how to work with Kotlin’s Boolean primitive type

Run BooleanExample.kt in your IDE and see the output for yourself.

Video

In the video, I’ll show you:

  • An overview of primitive data types
  • How to run the solution code for Unit 4
  • The Kotlin bytecode and decompile tools
  • How to debug in IntelliJ

Previous: Object- and function-oriented programming concepts and principlesNext: More Kotlin basics