Ten Kotlin Features To Boost Android Development

Introduction

A while ago, Tomasz introduced Kotlin development on Android. To remind you: Kotlin is a new programming language developed by Jetbrains, the company behind one of the most popular Java IDEs, IntelliJ IDEA. Like Java, Kotlin is a general-purpose language. Since it complies to the Java Virtual Machine (JVM) bytecode, it can be used side-by-side with Java, and it doesn’t come with a performance overhead.

In this article, I will cover the top ten useful features to boost your Android development.

Note: at the time of writing this article, actual versions were Android Studio 2.1.1. and Kotlin 1.0.2.

Tired of never ending Java code? Try Kotlin, and save your time and sanity.

Kotlin Setup

Since Kotlin is developed by JetBrains, it is well-supported in both Android Studio and IntelliJ.

The first step is to install Kotlin plugin. After successfully doing so, new actions will be available for converting your Java to Kotlin. Two new options are:

Create a new Android project and setup Kotlin in the project.

Add Kotlin support to an existing Android project.

To learn how to create a new Android project, check the official step by step guide. To add Kotlin support to a newly created or an existing project, open the find action dialog using Command + Shift + A on Mac or Ctrl + Shift + A on Windows/Linux, and invoke the Configure Kotlin in Project action.

To create a new Kotlin class, select:

File > New > Kotlin file/class, or

File > New > Kotlin activity

Alternatively, you can create a Java class and convert it to Kotlin using the action mentioned above. Remember, you can use it to convert any class, interface, enum or annotation, and this can be used to compare Java easily to Kotlin code.

Another useful element that saves a lot of typing are Kotlin extensions. To use them you have to apply another plugin in your module build.gradle file:

apply plugin: 'kotlin-android-extensions'

Caveat: if you are using the Kotlin plugin action to set up your project, it will put the following code in your top level build.gradle file:

To get the references for all the views in the layout with a defined ID, use the Android Kotlin extension Anko. Remember to type in this import statement:

import kotlinx.android.synthetic.main.activity_main.*

Note you don’t need to write semicolons at the end of the lines in Kotlin because they are optional.

The TextView from layout is imported as a TextView instance with the name equal to the ID of the view. Don’t be confused by the syntax, which is used to set the label:

helloWorldTextView.text = "Hello World!"

We will cover that shortly.

Caveats:

Make sure you import the correct layout, otherwise imported View references will have a null value.

When using fragments, make sure imported View references are used after the onCreateView() function call. Import the layout in onCreateView() function and use the View references to setup the UI in onViewCreated(). The references won’t be assigned before the onCreateView() method has finished.

Feature #2: Writing POJO Classes with Kotlin

Something that will save the most time with Kotlin is writing the POJO (Plain Old Java Object) classes used to hold data. For example, in the request and response body of a RESTful API. In applications that rely on RESTful API, there will be many classes like that.

In Kotlin, much is done for you, and the syntax is concise. For example, consider the following class in Java:

Well, that saves a lot of typing, doesn’t it? Let’s walk through the Kotlin code.

When defining variables in Kotlin, there are two options:

Mutable variables, defined by var keyword.

Immutable variables, defined by val keyword.

The next thing to note is the syntax differs a bit from Java; first, you declare the variable name and then follow with type. Also, by default, properties are non-null types, meaning that they can’t accept null value. To define a variable to accept a null value, a question mark must be added after the type. We will talk about this and null-safety in Kotlin later.

Another important thing to note is that Kotlin doesn’t have the ability to declare fields for the class; only properties can be defined. So, in this case, firstName and lastName are properties that have been assigned default getter/setter methods. As mentioned, in Kotlin, they are both public by default.

From the outside, when it comes to syntax, properties behave like public fields in Java:

val userName = user.firstName
user.firstName = "John"

Note that the new property fullName is read only (defined by val keyword) and has a custom getter; it simply appends first and last name.

All properties in Kotlin must be assigned when declared or are in a constructor. There are some cases when that isn’t convenient; for example, for properties that will be initialized via dependency injection. In that case, a lateinit modifier can be used. Here is an example:

When instantiating classes, note that Kotlin doesn’t have new keywords, as does Java. To instantiate the aforementioned User class, use:

val user = User("John", "Doe)

Introducing Inheritance

In Kotlin, all classes extend from Any, which is similar to Object in Java. By default, classes are closed, like final classes in Java. So, in order to extend a class, it has to be declared as open or abstract:

Feature #4: Lambda Expressions

Lambda expressions, introduced with Java 8, are one its favorite features. However, things are not so bright on Android, as it still only supports Java 7, and looks like Java 8 won’t be supported anytime soon. So, workarounds, such as Retrolambda, bring lambda expressions to Android.

With Kotlin, no additional libraries or workarounds are required.

Functions in Kotlin

Let’s start by quickly going over the function syntax in Kotlin:

fun add(x: Int, y: Int) : Int {
return x + y
}

The return value of the function can be omitted, and in that case, the function will return Int. It’s worth repeating that everything in Kotlin is an object, extended from Any, and there are no primitive types.

An argument of the function can have a default value, for example:

fun add(x: Int, y: Int = 1) : Int {
return x + y;
}

In that case, the add() function can be invoked by passing only the x argument. The equivalent Java code would be:

Wow! Just one line of code! We can see that the lambda expression is surrounded by curly braces. Parameters are declared first, and the body goes after the -> sign. With click listener, type for the view parameter isn’t specified since it can be inferred. The body is simply a call to toast() function for showing toast, which Kotlin provides.

Also, if parameters aren’t used, we can leave them out:

view.setOnClickListener({ toast("Click") })

Kotlin has optimized Java libraries, and any function that receives an interface with one method for an argument can be called with a function argument (instead of Interface).

Furthermore, if the function is the last parameter, it can be moved out of the parentheses:

view.setOnClickListener() { toast("Click") }

Finally, if the function has only one parameter that is a function, parentheses can be left out:

Extension Functions

Kotlin, similar to C#, provides the ability to extend existing classes with new functionality by using extension functions. For example, an extension method that would calculate the MD5 hash of a String:

The example function must be placed in a Utility class. What that means is that extension functions don’t modify the original extended class, but are a convenient way of writing utility methods.

Feature #5: Null-Safety

One of the things you hustle the most in Java is probably NullPointerException. Null-safety is a feature that has been integrated into the Kotlin language and is so implicit you usually won’t have to worry about. The official documentation states that the only possible causes of NullPointerExceptions are:

An explicit call to throw NullPointerException.

Using the !! operator (which I will explain later).

External Java code.

If the lateinit property is accessed in the constructor before it is initialized, anUninitializedPropertyAccessException will be thrown.

By default, all variables and properties in Kotlin are considered non-null (unable to hold a null value) if they are not explicitly declared as nullable. As already mentioned, to define a variable to accept a null value, a question mark must be added after the type. For example:

val number: Int? = null

However, note that the following code won’t compile:

val number: Int? = null
number.toString()

This is because the compiler performs null checks. To compile, a null check must be added:

val number: Int? = null
if(number != null) {
number.toString();
}

This code will compile successfully. What Kotlin does in the background, in this case, is that number becomes nun-null (Int instead of Int?) inside the if block.

The null check can be simplified using safe call operator (?.):

val number: Int? = null
number?.toString()

The second line will be executed only if the number is not null. You can even use the famous Elvis operator (?:):

If the expression on the left of ?: is not null, it is evaluated and returned. Otherwise, the result of the expression on the right is returned. Another neat thing is that you can use throw or return on the right-hand side of the Elvis operator since they are expressions in Kotlin. For example:

The !! Operator

If you want a NullPointerException thrown the same way as in Java, you can do that with the !! operator. The following code will throw a NullPointerException:

val number: Int? = null
number!!.toString()

Casting

Casting in done by using an as keyword:

val x: String = y as String

This is considered “Unsafe” casting, as it will throw ClassCastException if the cast is not possible, as Java does. There is a “Safe” cast operator that returns the null value instead of throwing an exception:

val x: String = y as? String

For more details on casting, check the Type Casts and Casts section of the official documentation, and for more details on null safety check the Null-Safety section.

lateinit properties

There is a case in which using lateinit properties can cause an exception similar to NullPointerException. Consider the following class:

This code will compile without warning. However, as soon as an instance of TestClass is created, an UninitializedPropertyAccessException will be thrown because property s is accessed before it is initialized.

Feature #6: Function with()

Function with() is useful and comes with the Kotlin standard library. It can be used to save some typing if you need to access many properties of an object. For example:

The example above shows an implementation of binary * operator on the String. For example, the following expression will assign value “TestTestTestTest” to a newString variable:

val newString = "Test" * 4

Since extension functions can be used, it means the default behavior of the operators for all the objects can be changed. This is a double-edged sword and should be used with caution. For a list of function names for all operators that can be overloaded, check the official documentation.

The example above prints a message when a property is assigned or read.

Delegates can be created for both mutable (var) and read-only (val) properties.

For a read-only property, getValue method must be implemented. It takes two parameters (taken from the offical documentation):

receiver - must be the same or a supertype of the property owner (for extension properties, it is the type being extended).

metadata - must be of type KProperty<*> or its supertype.

This function must return the same type as property, or its subtype.

For a mutable property, a delegate has to provide additionally a function named setValue that takes the following parameters:

receiver - same as for getValue().

metadata - same as for getValue().

new value - must be of the same type as a property or its supertype.

There are a few standard delegates that come with Kotlin that cover the most common situations:

Lazy

Observable

Vetoable

Lazy

Lazy is a standard delegate that takes a lambda expression as a parameter. The lambda expression passed is executed the first time getValue() method is called.

By default, the evaluation of lazy properties is synchronized. If you are not concerned with multi-threading, you can use lazy(LazyThreadSafetyMode.NONE) { … } to get extra performance.

Observable

The Delegates.observable() is for properties that should behave as Observables in Observer pattern. It accepts two parameters, the initial value and a function that has three arguments (property, old value, and new value).

The given lambda expression will be executed every time setValue() method is called:

class User {
var email: String by Delegates.observable("") {
prop, old, new ->
//handle the change from old to new value
}
}

Vetoable

This standard delegate is a special kind of Observable that lets you decide whether a new value assigned to a property will be stored or not. It can be used to check some conditions before assigning a value. As with Delegates.observable(), it accepts two parameters: the initial value, and a function.

The difference is that the function returns a Boolean value. If it returns true, the new value assigned to the property will be stored, or otherwise discarded.

Feature #9: Mapping an Object to a Map

A common use case is to store values of the properties inside a map. This often happens in applications that work with RESTful APIs and parses JSON objects. In this case, a map instance can be used as a delegate for a delegated property. An example from the official documentation:

To sum up, Kotlin offers you the ability to save time when writing native Android applications by using an intuitive and concise syntax. It is still a young programming language, but in my opinion, it is now stable enough to be used for building production apps.

The benefits of using Kotlin:

Support by Android Studio is seamless and excellent.

It is easy to convert an existing Java project to Kotlin.

Java and Kotlin code may coexist in the same project.

There is no speed overhead in the application.

The downsides:

Kotlin will add its libraries to the generated .apk, so the final .apk size will be about 300KB larger.

If abused, operator overloading can lead to unreadable code.

IDE and Autocomplete behaves a little slower when working with Kotlin than it does with pure Java Android projects.

Compilation times can be a bit longer.

About the author

As a software engineer and mobile enthusiast, Ivan has invested time and resources into perfecting the development process and architecture of mobile apps. With experience in building large and security sensitive applications, he has developed analytical skills, wireframing and prototyping techniques, and coding best practices to develop quality and secure software. [click to continue...]

Comments

Kirill Rakhman

> Make sure you import the correct layout, otherwise imported View references will have a null value.
That's actually not true. The generated code will just use refer to R.id.myView no matter what layout you import.

Martin Von Jazvec Petrulak

Great Article, iam just facing issues with code generation. Sometimes i need to run clean or run compiler twice in order to get recent changes. Are you facing same issues, or is there any hack to prevent this. Thanks

Apátrida

It would be clearer to say that `val` is `readonly property` similar to `final` in Java; and `var` is a `readwrite property` when talking about class properites. And the same as a variable `val` is equivalent to `final` variable whereas `var` is not. Saying one is immutable is misleading.

ilya g.

Do you know how to measure the extra performance you can get by using lazy(LazyThreadSafetyMode.NONE)?

Mrunal Khatri

Android development is trending now days and more and more people are getting involved in it. So the info here will help them a lot. Thanks

ariyagksk Tk

Hi Can you please let me know the tool used for the graphics in this artcile. I generally use visio and Creately

As a software engineer and mobile enthusiast, Ivan has invested time and resources into perfecting the development process and architecture of mobile apps. With experience in building large and security sensitive applications, he has developed analytical skills, wireframing and prototyping techniques, and coding best practices to develop quality and secure software.