The Programmers Guide To Kotlin - The Basics
Written by Mike James   
Monday, 01 August 2022
Article Index
The Programmers Guide To Kotlin - The Basics
Semicolons
Var & Val
Null Safety

Semicolons?

You might be wondering where the semicolon line ending has gone that is so much a feature of the majority of modern languages?

The simple answer is that Kotlin doesn’t need line ending indicators. In Kotlin the end of a line is the end of a line and you don’t need a special additional symbol to mark it. However, if you include more than one statement on a line you do need to indicate where each one ends by placing a semicolon between them, for example:

c=a+b; println(c); println(“Hello World”)

If you leave out those semicolons the compiler will prompt you to add them at the correct locations.

Kotlin is a little more sophisticated than you might think, because it isn’t just looking at line endings, it is inferring where commands finish – it is inferring semicolon positions from the grammar of the language. This means that, as well as not needing semicolons to make your meaning clear, you can split commands across lines without the need for a line continuation character.

For example, you can write the previous one-line example as:

c=a
+b
println(
        c
)
println(
        "Hello World")

If you try to put a line break within the “Hello World” in IntelliJ then it will even split the string and add a + between the two parts:

println(
        "Hello " +
                "World")

About the only thing you can’t do is split a keyword across lines – this breaks the grammar and stops the compiler inferring the semicolon positions.

If you are a Java, or other semicolon-heavy language, programmer then it can be difficult to stop using semicolons in Kotlin. The compiler will often point out when you have unnecessary semicolons and you can choose to remove them. The general rule is to stop adding semicolons and let the compiler tell you when what you have written is ambiguous and needs a semicolon.

Lambdas

Kotlin code generally makes a lot of use of lambdas and it is good to get to know the basics as early as possible. Lambdas are a key feature of Kotlin.

A lambda is essentially a function which you can store as a reference in a variable, or pass as a parameter to another function.

There are various ways to define a Kotlin lambda. The simplest is to write the parameters, complete with types, on the left of -> and the result on the right. The type of the result will be inferred:

val sum = { a: Int, b: Int -> a + b }
println(sum(1,2))

Notice that although sum behaves like a function, e.g. you can call it as in sum(1,2), it is just a variable that references the function and you can, for example, pass it as a parameter to another function.

You can also define the type of the lambda, as in:

val sum:(Int,Int)->Int = { a: Int, b: Int -> a + b }
println(sum(1,2))

This is the same as the first way, but now we have defined sum as a function type (Int,Int)->Int, i.e. a function that takes two integers and returns a single integer.

In this case we don't need the type declarations in the body of the lambda:

val sum:(Int,Int)->Int = { a, b ->  a + b }

The body of a lambda can have more than one expression and only the last is treated as the return value.

For example:

val sum:(Int,Int)->Int = { a, b -> 
 println(a)
 println(b)
 a+b
}
println(sum(1,2))

prints:

1, 2, 3.

As well as lambda functions you can also use anonymous functions, which look a lot like lambdas and hence can be confusing. An anonymous function is exactly what it sounds like – a standard function defined using fun, but one that doesn’t have a name.

For example:

fun(a: Int, b: Int): Int = a+b

or:

fun(a: Int, b: Int): Int{
 return a+b
}

As explained in Chapter 11, you can use an anonymous function more or less exactly as you would a lambda.

The whole point of lambdas and anonymous functions is that they can be passed as parameters to higher-order functions. To do this you have to specify the parameter that is used to pass the function as a function type.

For example:

val sum = { a: Int, b: Int -> a + b } 
fun doArithmetic(operation: (Int,Int)->Int){ println(operation(1,2)) }
doArithmetic(sum)

The doArithmetic function accepts a single parameter of a function type which specifies a function that accepts two Int parameters and returns a single Int.

Notice you can only pass a lambda or an anonymous function that matches. You can pass a standard function, but you have to use the :: reference operator, see Chapter 11.

There are lots more things to discover about functions including closure, inlining, destructuring and how objects can also be functions. These are explained in Chapters 10 and 11.

Infix Function Calls

Another nice feature of Kotlin that you will find in many examples is the ability to call functions using infix or operator notation.

If you precede a function with infix then you can call it as if it was an operator:

object1 function object2

is translated to:

object1.function(object2)

For example, if you have a function defined as a method of Myclass:

class MyClassA { 
      infix fun myFunc1(a: Int) {
            println(a)
      }
}
var myObject = MyClassA()

then you can call the method using the traditional:

myObject.myFunc1(3)

or using infix notation:

myObject myFunc1 3

Of course, this example isn't particularly sensible as an infix operation usually involves the objects on the left and the right.

All of the standard operators in Kotlin are implemented as infix functions. For example, you can write:

2+3

or:

2.plus(3)

If you are worried about efficiency considerations, it is worth knowing that the compiler translates these function calls to standard JVM operators. You can also override the functions that define the existing operators. The ability to create new infix operators presents an opportunity for writing things in very different ways in Kotlin.



Last Updated ( Tuesday, 02 August 2022 )