time

Getting started with Kotlin in Android

Getting started with Kotlin in Android

Introduction

Kotlin is a statically typed programming language from JetBrains. It is fully interoperable with Java (meaning you can use Java frameworks and even mix commands from both in your code) and comes with no limitations.

Advantages of Kotlin over Java are:

  • Kotlin is 100% interoperable with Java. You can call Java code from Kotlin and vice-versa easily. Both Kotlin and Java generate the same bytecode, so there’s no fear that you’re shipping something completely different with Kotlin. That means you can start using Kotlin in your existing project, with all your old Java code, right away.
  • No more NullPointerExceptions. NPE’s are the most common cause of crashing our app. It’s been a major time investment to fix all of the NPEs that have come up, as guarding your code with null checks everywhere is a time consuming and boring task. With Kotlin, you needn’t worry about NPEs. In Kotlin, It is so nice to catch NPEs at compile time instead of crashing apps at runtime.
  • Less code with readability. Kotlin can do the stuff same as Java, very less verbosity. Also, Kotlin is way more readable compared to Java.

For instance, consider this click listener in Java:

view.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View v) {
        // your code!
}});

and now consider the same thing in kotlin:

view.setOnClickListener { // your code!!! }

The good news is that seeing as Kotlin is packaged in with Android 3.0 onwards, there will be no need to download anything new and only very minimal set-up involved. The bad news is that Android Studio 3.0 is still in beta, so to get that you’re going to do steps from following section.

Setting up Android Studio for Kotlin development

The good people at JetBrains have created an IntelliJ/Android Studio Plugin for Kotlin. First, we’re going to go ahead and install the plugin.

To do this, navigate to File > Settings > Plugins > Browse Repositories and search for "Kotlin" then, click on Install.

When the install is complete, you will need to restart Android Studio to apply the new plugin.

Now that we have the plugin installed, let’s go ahead and create a new Android project - the usual way we would. Navigate to File > New > New Project and follow through project creation wizard. Select the Create Empty Activity option at the end.

Next step is to apply the Kotlin plugin to the project. There is an automated tool to do this but sometimes, the tool messes things up, so let’s just walk through the manual process of applying the plugin in our build.gradle files (both at the project level and the app-module level).

To configure the plugin, we first need to add the plugin to the root project’s build.gradle, same way it’s done for the Gradle plugin (automatically). Add the plugin to the dependencies closure in the project build.gradle file. The project build.gradle file looks like this:

buildscript {
    // replace with the latest version: https://github.com/JetBrains/kotlin/releases/latest
    ext.kotlin_version = "1.1.4"

    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

We created a variable ext.kotlin_version. The reason we have this is we may have other places in the project where we need to reference the Kotlin version, so it makes sense to "externalize" that value. We placed this variable in the buildscript closure. This is because, the buildscript is the entry point into this file when the project is being built. If we place the variable outside this closure, this variable won’t be available before the project is built, and the build will fail.

Second, apply the Kotlin Android Plugin. After adding the Kotlin Gradle plugin, the next step is to apply the plugin. To do this, we need to add apply plugin: kotlin-android to the app-module’s build.gradle file.

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

We’ve applied the plugin and setup all we need to, but our "Empty Activity" generated code is still in Java. But the Kotlin plugin can help us convert our code from Java to Kotlin.

To do this, select the file and navigate to Code > Convert Java File to Kotlin File. After converting our code to Kotlin, our empty activity looks like this:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

Kotlin basics

Kotlin has two keywords for declaring variables, val and var . The var is a mutable variable, which is, a variable that can be changed to another value by reassigning it. This is equivalent to declaring a variable in Java:

var name = "kotlin"

In addition, the var can be initialized later:

var name: String
name = "kotlin"

Variables defined with var can be reassigned, since they are mutable:

var name = "kotlin"
name = "more kotlin"

The keyword val is used to declare a read-only variable. This is equivalent to declaring a final variable in Java. A val must be initialized when it is created, since it cannot be changed later:

val name = "kotlin"

A read only variable does not mean the instance itself is automatically immutable. The instance may still allow its member variables to be changed via functions or properties, but the variable itself cannot change its value or be reassigned to another value.

Did you notice in the previous section that the type of the variable was not included when it was initialized? This is different to Java where the type of the variable must always accompany its declaration. Even though Kotlin is a strongly typed language, we don't always need to declare types explicitly. The compiler attempts to figure out the type of an expression from the information included in the expression. A simple val is an easy case for the compiler because the type is clear from the right-hand side. This mechanism is called type inference. This reduces boilerplate whilst keeping the type safety we expect of a modern language.

To create a number literal, use one of the following forms:

val int = 123
val long = 123456L
val double = 12.34
val float = 12.34F

You will notice that a long value requires the suffix L and a float, the suffix F. The double is used as the default for floating point numbers, and int for integral numbers.

Strings. Just as in Java, strings are immutable. String literals can be created using double quotes or triple quotes. Double quotes create an escaped string. In an escaped string, special characters, such as new line, must be escaped:

val string = "string with \n new line"

Triple quotes create a raw string. In a raw string, no escaping is necessary, and all characters can be included:

String templates are a simple and effective way of embedding values, variables, or even expressions inside a string without the need for pattern replacement or string concatenation. Usage is extremely straightforward. A value or variable can be embedded simply by prefixing with a dollar $ symbol:

val name = "Sam"
val str = "hello $name"

Arbitrary expressions can be embedded by prefixing with a dollar $ and wrapping in braces {}

val name = "Sam"
val str = "hello $name. Your name has ${name.length} characters"

Arrays. In Kotlin, we can create an array by using the library function arrayOf()

val array = arrayOf(1, 2, 3)

Alternatively, we can create an Array from an initial size and a function, which is used to generate each element:

val squares = Array(10, { k -> k * k })

Unlike Java, arrays are not treated as special by the language, and are regular collection classes. Instances of arrays provide an iterator function and a size function, as well as a get and a set function. The get and set functions are also available through bracket syntax like many C-style languages:

val element1 = array[0]
val element2 = array[1]
array[2] = 5

Ranges. A range is defined as an interval that has a start value and an end value. Any types which are comparable can be used to create a range, which is done using the .. operator

val aToZ = "a".."z"
val oneToNine = 1..9

Once a range is created, the in operator can be used to test whether a given value is included in the range. This is why the types must be comparable. For a value to be included in a range, it must be greater than or equal to the start value and less than or equal to the end value

val aToZ = "a".."z"
val isTrue = "c" in aToZ
val oneToNine = 1..9
val isFalse = 11 in oneToNine

There are further library functions to create ranges not covered by the .. operator, for example, downTo() will create a range counting down and rangeTo() will create a range up to a value. Both of these functions are defined as extension functions on numerical types:

val countingDown = 100.downTo(0)
val rangeTo = 10.rangeTo(20)

Once a range is created, you can modify the range, returning a new range. To modify the delta between each successive term in the range, we can use the step() function:

val oneToFifty = 1..50
val oddNumbers = oneToFifty.step(2)

Loops. Kotlin supports the usual duo of loop constructs found in most languages - the while loop and the for loop. The syntax for while loops in Kotlin will be familiar to most developers, as it is exactly the same as most C-style languages:

while (true) {
    println("This will print out for a long time!")
}

The Kotlin for loop is used to iterate over any object that defines a function or extension function with the name iterator. All collections provide this function:

val list = listOf(1, 2, 3, 4)
for (k in list) {
    println(k)
}

val set = setOf(1, 2, 3, 4)
for (k in set) {
    println(k)
}

Note the syntax using the keyword in. The in operator is always used with for loops. In addition to collections, integral ranges are directly supported either inline or defined outside

val oneToTen = 1..10
for (k in oneToTen) {
    for (j in 1..5) {
        println(k * j)
    }
}

Null safety

Kotlin eliminates most sources of null references by making all types non-nullable by default - meaning that the compiler won’t let you use a non-initialized, non-nullable variable.

This is one of the great things about Kotlin, everything in Kotlin is not nullable unless you specifically declare it in this way. The way to do this is with the ? question mark which also suggest you that the value could be there or not.

Java

if (text != null) {
    int length = text.length();
}

Kotlin

text?.let {
    val length = text.length
}

// or simple
val length = text?.length

There are a few ways to check and handle nullability on a type. Let’s discuss each of them using the String? example, where we want to use the strings length to control flow.

Explicit Checks. Just like Java, we could explicitly check for null:

val b: String? = "test"

if (b != null && b.length > 0) {
    // Do something with the string
} else {
    // String is null, handle error
}

Safe Operator. If we don’t want to deal with the verbosity of the above code, we can use the safe operator (?.) provided by Kotlin. This operator will either return the value if non-null, or null if it was unable to be read. Here is what the code would look like with and without the safe operator:

val length: Int? = if (b != null) b.length else null
val length: Int? = b?.length

As you can see, the safe operator makes the code a lot more concise. This becomes even more useful when you have a number of nested objects, and you are able to chain multiple safe operator calls together.

Elvis Operator. In Java, we may also be used to doing an explicit check for null and returning another value if it’s not found. For example, you may have written code like this in your lifetime:

return (myValue != null) ? myValue : -1;

Kotlin provides an elvis operator (?:) which works similar to the safe operator. However, instead of returning null, it’ll return the default value that you supply it. The above Java code would be written in Kotlin as:

return myValue ?: -1

Using that for the example further up regarding string length:

val length: Int = b?.length ?: 0
if (length > 0) doSomething() else fail()

Safe Class Casting. Similar to the safe operator for types, we can use the as? operator to safely cast objects. This will just return null if the cast is unsuccessful:

val intVal: Int? = myValue as? Int

Remember! In Kotlin you cannot leave a variable uninitialised, as you would do in Java, this is because of the fact that Kotlin doesn’t have NullPointerException, so to achieve that this is something that the language requires to have.

Extension functions

Coming from C#, extension functions is a great feature - and another that’s currently missing in Java. With Kotlin, you can add behavior to a class without directly extending it or using a design pattern like Decorator. Through an extension function, you can call a function from an object as if it were part of its class. We could create an extension function for String called makePretty, and call it myString.makePretty(). As we know, makePretty is not a member of String, but syntactically it looks like it is. So, how does it work? You just need to add the function to any file. In our makePretty example, it would look something like this:

fun String.makePretty(): String {
    // make the string pretty and return it
}

The key in the example above is the type qualifier before the function name. In Kotlin, it is compiled to a static function with a String parameter that returns a string and you can call it as if it were a member function, like myString.makePretty().

Data classes

This feature is a great time saver. Given that most of our applications are data driven, we often find ourselves creating classes with only properties and fields to hold data. In Java, this can be very tedious, requiring a get/set method for each field. With Kotlin, we can declare the class and all its properties in a single line. The compiler will generate all getters and setters, as well as the equality members, toString() and a copy() function.

For example, take this in Java:

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return this.age;
    }

    // and the methods equals, hashCode, copy, omitted for brevity
}

To run the same function in Kotlin, all we need is:

data class User(var name: String, var age: Int)

The compiler will generate everything we had to manually write in the above Java code.

Kotlin Android extensions

In Android, every time you want to work with any View in an Activity, you need to use the findViewById method to obtain a reference to that View. This makes findViewById one of the most important, but also one of the most frustrating bits of code that you’ll find yourself writing over and over, and over again in your Android projects. The findViewById method is a huge source of potential bugs, and if you’re working with multiple UI elements in the same Activity, then all those findViewByIds can really clutter up your code, making it difficult to read.

While there are a number of libraries, such as Butter Knife, that aim to remove the need for findViewByIds, these libraries still require you to annotate the fields for each View, which can lead to mistakes and still feels like a lot of effort that would be better invested in other areas of your project.

The Kotlin Android Extensions plugin (which has recently been incorporated into the standard Kotlin plugin) promises to make findViewById a thing of the past, offering you the benefits of the aforementioned libraries without the drawback of having to write any additional code or ship an additional runtime.

You can use Kotlin extensions to import View references into your source files. At this point the Kotlin plugin will create a set of synthetic properties that enable you to work with these views as though they were part of your activity - crucially, this means you no longer have to use findViewById to locate each View before you can work with it.

To use extensions, you’ll need to enable the Kotlin Android Extensions plugin in each module, so open your module-level build.gradle file and add the following:

apply plugin: 'kotlin-android-extensions'

Sync these changes by clicking the Sync Now popup.

You can then import the references to a single View, using the following format:

import kotlinx.android.synthetic.main.<layout>.<view-id>

For example, if your acitivity_main.xml file contained a TextView with the ID tvTitle, then you’d import the reference to this view by adding the following to your Activity

import kotlinx.android.synthetic.main.activity_main.tvTitle

You’d then be able to access tvTitle within this activity using its ID alone - and without a findViewById in sight.

Let’s take a look at extensions in action, by adding a TextView to our activity_main.xml file, importing it into our MainActivity.kt file, and using extensions to set the TextView's text programmatically.

Start by creating your TextView:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
    android:id="@+id/tvTitle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

</RelativeLayout>

You can then import the TextView into your MainActivity.kt, and set its text using its ID only

import kotlinx.android.synthetic.main.activity_main.tvTitle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        tvTitle.setText("Hello World")
    }
}

Note, if you want to work with multiple widgets from the same layout file, then you can import the entire contents of a layout file in one fell swoop, using the following formula:

import kotlinx.android.synthetic.main.<layout>.*

For example, if you wanted to import all the widgets from your activity_main.xml file, then you’d add the following to your Activity

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

Further reading

comments powered by Disqus