The Java-hikers guide to the Kotlin-axy
Here at Fourtress we are pros at coming up with creative titles, as you can see. We have embarked on a journey to become pros at Kotlin too. That’s why me and Stef Rombouts decided to write a series of introductory articles about Kotlin as we’re learning it ourselves. The articles will be about Kotlin and its syntax. But since we both have a Java background, we will also highlight some interesting differences between the two languages.
Part 1: Variables and functions
This part will be about variables and functions. We will cover the basics of variables, null handling, lateinit, accessors and backing fields. Then we will tell more about the basics of functions, anonymous functions, extension functions and a bit about lambdas.
In Kotlin, variables are created differently compared to Java. It is not necessary to declare what kind of type you are creating. The compiler understands this by itself. This is called Type Inference.
In the example above a variable is created with the val keyword. Since it is filled with a string literal, the compiler automatically handles it as a String type. The only requirement for Type Inference to work is that you initialise the variable right away. Just declaring it would not be enough.
Of course it is still possible to specify the type of a variable explicitly. A use case could be when you want to assign a number to a variable but want the compiler to handle it as a short instead of an integer.
Without the specified Short type, the compiler would handle the above example as an integer.
When using val the variable becomes final, which means that behind the scenes a getter is automatically generated. Since it is final, no setter is generated.
Here you see the first example again, with one difference: var is used instead of val. This is done when the type should not be final. When using var, both a setter and a getter are generated.
You may be wondering how you can use the generated accessors.
As shown in above example, accessing engine.engineCode will call the generated getter for engineCode behind the scenes. This works in a similar way for setters. Assigning a value to a variable calls the generated setter.
Null handling and lateinit
As a Java developer you will probably be used to having the declaration and initialisation of variables separate from each other. Since Kotlin is more strict when it comes to NullPointerException prevention it will not let you separate the declaration and initialisation like you are used to in Java. Every variable has to be initialised together with its declaration, or in an init block (we will get back to those in the next article). Of course, there are cases where a variable simply can’t have a meaningful value right away.
The first option you have then is to use a nullable variable. This is also useful when you’re not entirely sure that a property will not be null in the future. You can do this by putting a question mark behind the type:
In above example, you can see that an instance of vehicle has a nullable engine object. It can be null since not every vehicle has an engine.
Keep in mind that if you tell the compiler that a variable is nullable, it will also be handled as a nullable variable. When you want to access properties or call functions of a nullable object instance, you need to use the question mark again:
In this case, when the vehicle does not have an engine and this property is null, revEngine() will not be executed and null will be returned. These so called Safe Calls can also be chained.
Another way to handle an optional variable is to force the compiler to assume that the variable is not null by using the not-null assertion operator (!!):
If you can appreciate a good NPE every now and then, this is the operator to go with. However, it is not recommended 🙂
The second option to deal with variables that can’t be initialised right away is to use lateinit:
Lateinit (stands for late initialisation) can be used when you are sure that the variable will be initialised before it is used, but it can’t be initialised together with the declaration. This can only be used in combination with var. An example from the Android world would be when you declare a public Button property which will be initialised in the onCreate method. Keep in mind that if you do not initialise this property, the application will crash the moment you are trying to access it.
Get() and Set()
Like previously mentioned, Kotlin will automatically generate accessors for variables behind the scenes. For val properties only a getter and for var properties both a setter and getter are generated.
As developers we would like to have the ability to customize the getters and setters if needed. But how are we going to do that when the getters and setters are generated behind the scenes?
For each property you have the possibility to define custom accessor(s). For example a custom getter could look like this:
Each time fullEngineCode is accessed this custom getter will be called. In this example, it will return a single string which contains a combination of the prefix and the engineCode itself.
Below is an example of a custom setter:
Each time a new value is assigned to engineCode, the setter will be called where value contains the newly assigned value. In this example, setting engineCode to “14-X” would result in engineCode being changed to “14X”.
You might be wondering where field suddenly comes from. This is called a Backing Field. These can only be referenced from the accessors and will be generated only when the default implementation of the accessors are used, or when a custom accessor references it through the field identifier. It can be seen as a representation of the property inside the accessors. If, instead of the backing field, engineCode would be used inside the custom setter in above example, it would become recursive since the setter would be called again from the setter. Therefore field should be used.
If you would like to read about this in more detail, please check out this article.
Functions in Kotlin are declared with the fun keyword. The return type is specified at the end. Functions that return nothing don’t need to specify a return type.
Here’s how a basic function looks:
And here’s a function with a String return type:
If you want to make it explicit that a function doesn’t return any useful value, you can use Unit (this would be the equivalent of void in Java). A return statement is also not necessary in the body of Unit functions, just like in Java.
Unit is defined as a type with one value: the Unit object. The object keyword is generally used for Singletons in Kotlin. Below is the implementation of Unit in the Kotlin stdlib:
This makes it possible to write the following piece of code:
You could also make a list of Units, write extension functions for Unit or do just about anything that you can do with any type in Kotlin. The usefulness of these examples is pretty debatable, but it won’t hurt to know that Unit differs from void.
Single expression functions
Single expression functions are, well, you guessed it, functions that consist of one single expression. Since the body of these functions are – or at least should be – so short, curly brackets are omitted. Instead, an equals symbol is used:
What’s also nice about single expression functions is that the return type doesn’t need to be specified. The compiler understands that the following function returns an Int:
Extension functions can be used to add functionality to a class without changing the class itself. Let’s say you have the following RaceCar class in a library that you can’t edit.
This car can start, but what if you want it to drive too? In Java you would probably create a CarUtils class with a static function inside that looked something like this:
Using this function would look like CarUtils.makeCarDrive(myCar). Wouldn’t it be great if you could just say myCar.drive()? That’s where extension functions come in:
As you can see, the only difference from a normal function is that you put “RaceCar.” in front of the function name. The RaceCar instance that this function is called on (myCar) can then be used inside the function by using the this keyword, which feels very natural.
Extension functions can also be used on generic types:
This function can then be called on any object:
First-class citizens & Higher-order functions
If you ever read about functions in Kotlin, you will probably see the terms first-class citizens and higher-order functions being used. Don’t let these terms intimidate you, they’re not that complicated. First-class means that functions have a special status in Kotlin, there’s a function type. This enables you to pass functions as arguments to other functions, return them from other functions or store them in variables. Functions that take functions as arguments or return another function are called higher-order functions.
Suppose we have this overly simplified version of a View class on which you can set a click listener:
The setOnClickListener function is a higher-order function because it accepts a parameter with a function type. Calling of that function would be done in the following way:
In Kotlin, if the last argument that a function expects is of the function type, you can put the lambda in curly brackets after the function call, behind all the other arguments in parentheses. If there are no other arguments, which is the case here, you can even omit parentheses.
Function lambdas and anonymous functions
Below is a function stored in a variable, done in two different ways.
The first one uses the lambda notation, similar to the call site of setOnClickListener above. The second one is called an anonymous function. Notice that it’s more explicit in that you need to specify the return type and also need to use an explicit return statement. In the first example, the last expression in the function body is always returned. Since there is just one expression (x + y), the return value is the result of it. The biggest difference between the two notations is in fact the explicit return statement. This makes an anonymous function desirable when the body is long or when there are multiple return statements used, especially if some of those statements are inside of other lambdas used within the body. If this sounds a bit fuzzy, check out this article that explains it in more detail.
You can call these functions just like regular functions:
That concludes our first article about Kotlin. In the next article we will cover constructors and companion objects, so stay tuned!
– Written by Ahmet and Stef.