Unit objectives

In this unit, you will:

  • Understand type inference and static typing
  • Understand variables and the different ways to declare them
  • Learn how to use Kotlin’s binary and unary arithmetic operators
  • Learn basic function syntax

Type inference

The Kotlin compiler can deduce the type of an expression based on what values are used in it. This is called type inference and because of it, the type of every expression is known at compile type.

Static typing

In general,

  • Static refers to compile time and
  • Dynamic refers to runtime

So a statically typed language is one where the compiler knows the types of all expressions (variables, function return values, and so forth) when the code is compiled.

Contrast this with a dynamically typed language (like JavaScript) in which the types are deduced at runtime. This can lead to the dreaded (and at times very hard-to-track-down) undefined error message.

Kotlin is a statically typed language, which means the compiler must know the type of every value used in your program at compile time. If you’ve worked with statically typed languages before (like Java, or C), you are familiar with the requirement to specify the type of a variable when you declare it. Statically typed languages require all variable declarations to include a type annotation which tells the compiler the variable’s type.

Kotlin doesn’t require you to declare the type of every object you use since it can use type inference to figure out all expression types and thus fulfill its static typing requirements.

The result: you write cleaner code, and less of it.

In case of emergency

There are, however, times when the compiler cannot infer the type of an expression and you get an error message. When this happens, you can figure out what to do if you have a basic understanding of type inference rules. I’ve provided several type inference examples in this unit to prepare you for those occasions when you’ve asked the compiler to make an inference and it is unable to do so.

Variables

A variable is a named value used by your program. It is declared with a name and optional type and assigned a value. The name serves as a “handle” for the value in your code.

Kotlin supports two types of variable declarations:

  • Immutable variables or val
  • Mutable variables or var
Immutable variables?
A variable (mathematically speaking) has a value that varies. The idea that a variable is immutable seems, well, wrong. However, this terminology is commonly used and is consistent with the Kotlin documentation and I’ll use it throughout the learning path for those reasons.

val

A val variable is immutable; it cannot be assigned a different value after it is initialized. val is short for “value,” indicating its nature: Like a value, it cannot be changed.

Here are some examples:

val byte10: Byte = 10
val shortTen: Short = 10
val int10: Int = 10
val longTen: Long = 10L
val floatTen: Float = 10.0f
val double10: Double = 10.0
val charA: Char = 'a'
val booleanTrue: Boolean = true
val greeting: String = "Hello"
val anything: Any = 10

Listing 1. val declarations with explicit type annotations

Use the javaClass extension property to see the underlying Java type the Kotlin compiler uses (recall Table 1 from Unit 4):

$ kotlinc
Welcome to Kotlin version 1.2.61 (JRE 1.8.0_181-b13)
Type :help for help, :quit for quit
>>> val byte10: Byte = 10
>>> val shortTen: Short = 10
>>> val int10: Int = 10
>>> val longTen: Long = 10L
>>> val floatTen: Float = 10.0f
>>> val double10: Double = 10.0
>>> val charA: Char = 'a'
>>> val booleanTrue: Boolean = true
>>> val greeting: String = "Hello"
>>> val anything: Any = 10
>>> byte10.javaClass
byte
>>> shortTen.javaClass
short
>>> int10.javaClass
int
>>> longTen.javaClass
long
>>> floatTen.javaClass
float
>>> double10.javaClass
double
>>> charA.javaClass
char
>>> booleanTrue.javaClass
boolean
>>> greeting.javaClass
class java.lang.String
>>> anything.javaClass
class java.lang.Integer

You can omit the type annotation from a val declaration and the compiler will infer the type:

val byte10 = 10
val shortTen = 10
val int10 = 10
val longTen = 10L
val floatTen = 10.0f
val double10 = 10.0
val charA = 'a'
val booleanTrue = true
val greeting = "Hello"
val anything = 10

Listing 2. val declarations without explicit type annotations; the compiler uses type inference in all these cases

The compiler uses the type of the literal on the right-hand side of the assignment when it performs type inference (see Unit 4, Table 2).

The val declaration requires a value when it is declared. You will get an error message if you try and declare a val without assigning it an initial value:

>>> val someValue
error: property must be initialized or be abstract
val someValue
^

>>>

You will get an error message if you attempt to reassign the value in a val:

>>> val byte10 = 10
>>> byte10 = 16
error: val cannot be reassigned
byte10 = 16
^

>>>
Tip: Use val by default
Make a habit of declaring all of your variables with val. Recall that one of the core concepts of functional programming is immutability. You cannot change a variable that is declared using val and it is therefore immutable.

Exercise 1

Enter the declarations from Listing 2 into the REPL. Tell the REPL to evaluate each val‘s javaClass property and it will show you the underlying Java class (or primitive type) that corresponds to each literal value. Can you explain the compiler’s choices?

Check your answer

The following output is what I see when I enter the val declarations from Listing 2 into the REPL then get the javaClass property value for each.


$ kotlinc
Welcome to Kotlin version 1.2.61 (JRE 1.8.0_181-b13)
Type :help for help, :quit for quit
>>> val byte10 = 10
>>> val shortTen = 10
>>> val int10 = 10
>>> val longTen = 10L
>>> val floatTen = 10.0f
>>> val double10 = 10.0
>>> val charA = 'a'
>>> val booleanTrue = true
>>> val greeting = "Hello"
>>> val anything = 10
>>> byte10.javaClass
int
>>> shortTen.javaClass
int
>>> int10.javaClass
int
>>> longTen.javaClass
long
>>> floatTen.javaClass
float
>>> double10.javaClass
double
>>> charA.javaClass
char
>>> booleanTrue.javaClass
boolean
>>> greeting.javaClass
class java.lang.String
>>> anything.javaClass
int
>>>

Most of the compiler’s choices are exactly as we would expect. The literal 10 has a type of Int, so the Java int primitive was chosen (same for shortTen, int10, and anything). The literal 10L (whose type is Long) was used to initialize longTen, so no surprise there. floatTen was initialized with 10.0f, whose type is Float. The literal 10.0 has a type of Double, and the compiler chose double as the underlying Java primitive.


var

A var variable is mutable and can be assigned a different value after it is initialized. var is short for “variable,” indicating its nature. That it, like a mathematics variable, can be changed.

Here are some examples:

var byte10: Byte = 10
var shortTen: Short = 10
var int10: Int = 10
var longTen: Long = 10L
var floatTen: Float = 10.0f
var double10: Double = 10.0
var charA: Char = 'a'
var booleanTrue: Boolean = true
var greeting: String = "Hello"
var anything: Any = 10

Listing 3. var declarations with explicit type annotations

Like val, vars can be initialized using type inference:

var byte10 = 10
var shortTen = 10
var int10 = 10
var longTen = 10L
var floatTen = 10.0f
var double10 = 10.0
var charA = 'a'
var booleanTrue = true
var greeting = "Hello"
var anything = 10

Listing 4. var declarations without explicit type annotations; the compiler uses type inference in these cases

As you would expect, you may reassign a var as much as you like (after all, that’s what it’s for); however, the new type must be compatible with the basic type that was used to initialize the var or you will get an error:

$ kotlinc
Welcome to Kotlin version 1.2.61 (JRE 1.8.0_181-b13)
Type :help for help, :quit for quit
>>> var myVariable = 20
>>> myVariable.javaClass
int
>>> myVariable
20
>>> myVariable = 2000
>>> myVariable
2000
>>> myVariable = 0xffff
>>> myVariable
65535
>>> myVariable = 1.23
error: the floating-point literal does not conform to the expected type Int
myVariable = 1.23
             ^

>>> myVariable = "Hello"
error: type mismatch: inferred type is String but Int was expected
myVariable = "Hello"
             ^

>>>

In this example, the compiler first creates myVariable as an Int (the first inference made sets the type of the newly created variable). Later, when you try to assign a Float value, the compiler complains. You will get the same result when you try to assign a String literal to myVariable.

Kotlin operators

Kotlin provides several types of operators:

  • Binary arithmetic operators
  • Unary operators
  • Shift operators

Binary arithmetic operators

Kotlin provides these binary arithmetic operators:

Operation Operator Example Description
Multiplication * a * b Computes the product of a and b
Division / a / b Computes the quotient of a (numerator) and b (denominator)
Modulus % a % b Computes the remainder of the quotient of a (numerator) and b (denominator)
Addition + a + b Computes the sum of a and b
Subtraction - a - b Computes the difference between a and b
Augmented multiplication assignment *= a *= b Computes the product of a and b and assigns the result to a
Augmented division assignment /= a /= b Computes the quotient of a (numerator) and b (denominator) and assigns the result to a
Augmented addition assignment += a += b Computes the sum of a and b and assigns the result to a
Augmented subtraction assignment -= a -= b Computes the difference between a and b and assigns the result to a

Table 1. Kotlin’s arithmetic operators

The arithmetic operators are binary operators and require two operands, symbolized by a and b in Table 1.

Please Excuse My Dear Aunt Sally (PEMDAS)
The rules of operator precedence in Kotlin follow the standard order of operations from mathematics (PEMDAS) and are explained in detail in the Kotlin grammar documentation.

Exercise 2

Use the REPL for this exercise.

Declare a var called a and initialize it to 10. Declare a val called b and initialize it to 1. Write code to exercise the operations in Table 1 using a and b. Write one line of code for each row in Table 1 to perform the operation in that row. For assignment operations, you will have to tell the REPL evaluate a (why is that?).

Check your answer

You should see the following output:


$ kotlinc
Welcome to Kotlin version 1.2.61 (JRE 1.8.0_181-b13)
Type :help for help, :quit for quit
>>> var a = 10
>>> var b = 3
>>> a  3
30
>>> a / 3
3
>>> a % b
1
>>> a + b
13
>>> a - b
7
>>> a = b
>>> a
30
>>> a /= b
>>> a
10
>>> a += b
>>> a
13
>>> a -= b
>>> a
10
>>>

Question: Why do you have to tell the REPL to evaluate a for the assignment operations?

Answer: Because the assignment (for example, a *= b) is a statement, which does not return a value. The other binary operators (for example, a + b) are expressions.


Unary operators

Kotlin also has a set of unary operators, each of which requires only one operand. The unary operators are summarized in Table 2.

Operation Operator Example Description
Increment ++ a++ Prefix increment syntax: Expression evaluates to the current value of a, then a + 1 is computed and assigned to a
Increment ++ ++a Postfix increment syntax: a + 1 is computed and assigned to a, then the expression evaluates to modified value of a
Decrement -- a-- Prefix decrement syntax: Expression evaluates to the current value of a, then a - 1 is computed and assigned to a
Decrement -- --a Postfix decrement syntax: a - 1 is computed and assigned to a, then the expression evaluates to modified value of a
Unary Plus + +a Returns positive a (a must be numeric)
Unary Minus - -a Returns negative a (a must be numeric)
Not ! !a Returns “not a” (a must be of type Boolean)

Table 2. Kotlin’s unary operators

The prefix versus postfix increment/decrement operations are confusing at first, so let’s look at some examples.

I’ll use the REPL for these and I suggest that after reading this section, you go back and try these out for yourself.

I’ve added line numbers to the listing. The line numbers will not appear in the REPL; I’ve added them to help in my explanation.

01 >>> var a = 1
02 >>> a++
03 1
04 >>> a
05 2
06 >>> ++a
07 3
08 >>> a
09 3
10 >>> a--
11 3
12 >>> a
13 2
14 >>> --a
15 1
16 >>> a
17 1
18 >>>

Listing 5. demonstration of the prefix and postfix increment and decrement operators

Lines Example
1 Declare a var called a and initialize it to 1.
2 Use the postfix increment operator ++.
3 The expression evaluates to the original value of a.
4 Ask the REPL to evaluate a.
5 a evaluates to the incremented value.
6 The prefix increment operator is used on a.
7 a evaluates to the incremented value.
8-9 Verify that the prefix increment worked as expected.
10-17 Same as previous, but for the decrement operator.

Check out the Kotlin increments and decrements documentation if you would like a deeper look at the operators in Table 2.

Exercise 3

Use the REPL for this exercise.

Declare a var called a of type Int and assign it a value of 12. Demonstrate the plus, minus, and not unary operators from Table 2. Which operators do not work? Why not?

Check your answer

You should see the following output:


$ kotlinc
Welcome to Kotlin version 1.2.61 (JRE 1.8.0_181-b13)
Type :help for help, :quit for quit
>>> var a = 12
>>> +a
12
>>> -a
-12
>>> !a
error: unresolved reference: !
!a
^

>>>

The unary not operator only works for values of type Boolean.


Shift operators

Shift operators are used to perform bitwise operations on Int and Long numeric values. The concept of bit operations is an advanced topic for this course, but I want you to be aware that they exist.

If you’re familiar with Java, you may have seen its signed shift left (<<), signed shift right (>>) and unsigned shift right (>>>) operators. Kotlin does not support these symbols for performing bitwise operations.

Instead, shift operations are performed through function calls, which are summarized in Table 3.

Operator Function Example Description
Signed shift left shl 1024 shl 1 Shift all bits (except the sign bit) in the left operand by the number of bits (right operand) to the left. The sign bit is always preserved.
Signed shift right shr 1024 shr 1 Shift all bits (except the sign bit) in the left operand by the number of bits (right operand) to the right. The sign bit is always preserved.
Unsigned shift right ushr -1 ushr 1 Shift all bits (including the sign bit) in the left operand by the number of bits (right operand) to the right. The sign is not preserved.
Bitwise AND and 0xffff and 0xf0f0 Compares each bit in the left operand with the corresponding bit in the right operand. If the bits are both 1, the resuling bit is 1, otherwise it is 0.
Bitwise OR or 0xffff or 0xf0f0 Compares each bit in the left operand with the corresponding bit in the right operand. If the bits are different or both 1, the resulting bit is 1, otherwise it is 0.
Bitwise exclusive OR xor 0xffff xor 0xf0f0 Compares each bit in the left operand with the corresponding bit in the right operand. If the bits are different, the resulting bit is 1, otherwise is it 0.

Table 3. Kotlin’s shift operator functions

Kotlin’s shift operators work just like their Java counterparts. Check out this article for a detailed discussion of Java’s shift operators.

Exercise 4

Use the REPL to complete this exercise.

Write code to evaluate each of the example expressions from Table 3. Explain the results you get.

Check your answer

You should see the following output:


>>> 1024 shl 1
2048
>>> 1024 shr 1
512
>>> -1 ushr 1
2147483647
>>> 0xffff and 0xf0f0
61680
>>> 0xffff or 0xf0f0
65535
>>> 0xffff xor 0xf0f0
3855
>>>

  • Shifting 1024 to the left by 1 bit multiplies it by 2: 1024 * 2 = 2048
  • Shifting 1024 to the right by 1 bit divides it by 2: 1024 / 2 = 512
  • Shifting -1 (0xffffffff) to the right, including the sign bit, is 2147483647 (0x7fffffff)
  • 0xffff AND 0xf0f0 results in 0xf0f0 which is 61680
  • 0xffff OR 0xf0f0 results in 0xffff, which is 65535
  • 0xffff XOR 0xf0f0 results in 0x0f0f, which is 3855


Functions

A function is a block of code that does something useful. A function takes zero or more parameters, uses those parameters to perform a computation, and optionally returns the results to the caller. A function is not required to return a value.

The point of no return
Your Kotlin functions are not required to return a value, although every Kotlin function must return something. Wait, what? If your function returns nothing useful, it actually returns the Unit type, which is used to indicate that the function doesn’t return anything useful. This still meets the requirement that all functions return something. We’ll talk more about the Unit type in Unit 7.

You declare a function with the fun keyword, along with the function’s parameter list and return type, and include a block of code, like this:

fun saySomething(name: String): String {
    return "Hello there, $name"
}

The parameter list is name: String which tells the compiler “a val called name of type String“.

The return type is of type String following the colon.

To call the function, you specify the name of the function, and pass any parameters, like this:

saySomething("Steve")

Type the function into the REPL and the line of code to call it then the results look like this:

$ kotlinc
Welcome to Kotlin version 1.2.61 (JRE 1.8.0_181-b13)
Type :help for help, :quit for quit
>>> fun saySomething(name: String): String {
...     return "Hello there, $name"
... }
>>> saySomething("Steve")
Hello there, Steve
>>>

The saySomething function is called a single expression function, which means it does its useful thing in a single expression. Rewrite it like this:

fun saySomething(name: String) = "Hello there, $name"

Enter the new declaration into the REPL (you don’t need to exit the REPL; the new function declaration overwrites the one from earlier) along with the line of code to call it and you will see this:

>>> fun saySomething(name: String) = "Hello there, $name"
>>> saySomething("Steve")
Hello there, Steve

You get an error message if you don’t pass all of the required parameters:

saySomething()
error: no value passed for parameter 'name'
saySomething()
             ^

>>>

A function can have as many parameters as you need. The parameters in the parameter list are separated by commas:

saySomething(name: String, country: String) = "Hello there, $name, you are from $country"

You see this when you run it in the REPL:

>>> fun saySomething(name: String, country: String) = "Hello there $name, you are from $country"
>>> saySomething("Steve", "USA")
Hello there Steve, you are from USA
>>>

We will look at functions in more detail in Unit 7.

Conclusion

In this unit, you have learned:

  • How type inference works
  • How to declare immutable and mutable variables
  • How to work with Kotlin’s binary and unary operators
  • How to declare simple functions

Test your understanding

(Hint: Use the REPL to help you answer the code-related questions.)

True or False

  1. The following code produces a compiler error. (Explain your answer.)

     val a = 10
     var b = 10
     b++
     a++
    
  2. The following code produces a compiler error. (Explain your answer.)

    var a = 10
    println("The value of a is " + a)
    a++
    println("The value of a is now $a")
    a = "Success!"
    println(a)
    
  3. All of the following are legal variable declarations. (Explain your answer.)

    var apples = "Oranges"
    val theMeaningOfLife: Char = "42"
    val bananas = 10.0f
    var fruit: Int = 16
    val dubbel: Double = 51.0
    
    Check your answers

    1. True

    The postfix decrement operator returns the value of a as the value of the expression then increments the value of a and assigns it to a. Since a is declared as a val, its value cannot be changed and generates a compiler error:

      
      >>> val a = 10
      >>> var b = 10
      >>> b++
      10
      >>> a++
      error: val cannot be reassigned
      a++
      ^
    
      >>>
      
      

    Since b is declared as a var, there is nothing wrong with using the postfix decrement operator on it.

    2. True

    a has an inferred type Int. The attempt to assign the literal "Success!" (which is of type String) produces a compiler error:

      
      >>> var a = 10
      >>> println("The value of a is " + a)
      The value of a is 10
      >>> a++
      10
      >>> println("The value of a is now $a")
      The value of a is now 11
      >>> a = "Success!"
      error: type mismatch: inferred type is String but Int was expected
      a = "Success!"
          ^
    
      >>> println(a)
      11
      >>>
      
      

    3. False

    All of the declarations are legal except theMeaningOfLife: Char = "42", which produces the following error:

      
      >>> val theMeaningOfLife: Char = "42"
      error: type mismatch: inferred type is String but Char was expected
      val theMeaningOfLife: Char = "42"
                                   ^
      
      

    You may not assign a String to a variable of type Char.

Multiple choice (explain your answer)

  1. Which of the following statements describes features of a statically typed programming language? Choose all that apply.

    A. Variables must be declared at the top of the program.

    B. The variable type must be known at runtime.

    C. Variable values cannot be changed once they are initialized.

    D. The type of a variable must be known at compile time.

    E. The compiler performs type inference for all variables at compile time.

  2. Which of the following statements accurately describes how type inference is performed?

    A. A variable’s type is inferred by the compiler based on how it is initialized.

    B. A dynamically typed language can never accurately determine the type of a variable.

    C. The compiler can infer the type of a literal expression.

    D. An inferred type cannot be changed once it is initialized.

  3. Which of the following code snippets are immutable variable declarations?

    A. var a: String = "This value cannot be changed!"

    B. val pi = 3.14159

    C. a = "This is temporary"

    D. var pi = 3.14159

    Check your answers

    1. D, E. The type of a variable must be known at compile time. Whether the compiler performs type inference or the programmer specifies it explicitly makes no difference.

    2. A, C. The compiler can infer the type of a variable based on how it is initialized, either with another variable value of a known type or via a literal whose type may be inferred.

    3. B. An immutable variable is declared using the val keyword. Choices A and D are mutable variables (declared using var) and choice C is not a valid declaration at all.

Short answer

  1. What happens when you run the following code?

    val a: Char = 'a'
    var b = 'b'
    a = b
    
  2. Can you spot the error in the following code?

    val c = 42
    
    fun a(a: Int, b: String) {
        println("The value of a is $a and the value of b is '" + b + "' and c is $c")
    }
    
    a(41, "Value of b, lol")
    
  3. Can you spot the error in the following code?

    val c = 42
    
    fun funkyFunc(a: Int, b: String) {
        println("The value of a is $a and the value of b is '" + b + "' and c is $c")
    }
    
    funkyFunc(41, "Value of b, lol")
    funkyFunc(41)
    
  4. What is the value of a when the following code has finished running?

    var a = 1
    a++
    a += 20
    a *= 3
    println("The value of a is " + a++)
    
    Check your answers

    1. You get a compiler error.

    You will get a compiler error when trying to assign a the value of b because it is declared as a val (which cannot be changed). The error message is:

      
      >>> val a: Char = 'a'
      >>> var b = 'b'
      >>> a = b
      error: val cannot be reassigned
      a = b
      ^
      
      

    2. The code is clean.

    This is a trick question. (Copy and paste it into the REPL to see for yourself). You might think the function a having the same name as its parameter a may cause a problem, but this is perfectly legal.

    3. It produces an error message.

    The function call funkyFunc(41) does not contain the required number of arguments and produces the following error message:

      
      >>> funkyFunc(41)
      error: no value passed for parameter 'b'
      funkyFunc(41)
                  ^
      
      

    4. The value of a is 67.

    The value of a is 67 when the code finishes running. The println call prints The value of a is 66, but notice what’s happening with the postfix increment operator: It changes the value to 67 after it is returned to println (the final value of a is just not printed to the console, so you don’t see it).

    If you paste the code into the REPL, you get this (I ask the REPL to evaluate a when the code finishes running):

      
      >>> var a = 1
      >>> a++
      1
      >>> a += 20
      >>> a *= 3
      >>> println("The value of a is " + a++)
      The value of a is 66
      >>> a
      67
      >>>
      
      

Previous: Get started with KotlinNext: Your first Kotlin class