Android Development

The Benefits of Android Development in Kotlin

February 2, 2017

Kotlin is a relatively new language for the Java Virtual Machine that makes Android development nicer and streamlined in many ways. We’ve begun to use Kotlin in many of our Android apps and have found many advantages both in development time and quality. Kotlin is a language that features built-in null checking, extension methods for adding functionality to existing classes, and it is fully interoperable with Java code. Below are only a few of my favorite features of Kotlin that are an improvement upon Android development in Java.

Nullability

In Java, all objects are nullable and can be assigned to null and methods can return null. Without rigorous null-checks, this can lead to NullPointerExceptions and can necessitate long annoying chains of null-checks to safely look deep into an object.

Order order = getOrderOrMaybeNull();
String orderCity = null;

if (order != null) {
 if (order.getCustomer() != null) {
 if (order.getCustomer().getAddress() != null) {
 orderCity = order.getCustomer().getAddress().getCity();
 }
 }
}

All this null-checking cruft can make it tempting to leave these null checks out and make the dangerous assumption that “these will almost definitely (probably) never be null”.

Kotlin denotes nullable objects and operators with ? marks. A variable is declared to be nullable by appending a question mark to its type.

// non-null type, can never be null, and must be given a value when defined
var someObject: SomeObject = SomeObject()

// nullable type, can be null, denoted by the ? mark
var someObject: SomeObject? = null

Accessing methods and fields in a nullable object necessitates the use of the safe call operator (?.). This allows us to express the above deep-look into an object in a far less verbose way.

var orderCity = orderOrMaybeNull?.customer?.address?.city

Much more concise, readable, and the null-safety is enforced by the language. Should any of the objects in the chain be null, orderCity will evaluate to null, with no NullPointerExceptions being thrown.

You’ll also notice that Kotlin does away with the verbosity of getter and setter methods and lets you access class properties by their name, with optional getter and setter methods. A Kotlin class will still provide access to these properties through getSomeObject() methods when being referenced from Java code.

Nullability annotations

Java types are treated by Kotlin as not strictly null or non-null and are exempt from Kotlin’s nullability enforcement. This means that if a Java type is treated by Kotlin code as non-null but ends up being null, then a NullPointerException will happen. However, the Kotlin compiler and Kotlin-supported IDEs can recognize nullability annotations that indicate if a method has arguments and a return type that are nullable or not.

Here’s an example of using the Android support library nullability annotations in Java code.

import android.support.annotation.Nullable;

public class Utils {

 @Nullable
 public static String getBooleanAsString(@Nullable Boolean bool) {
 if (bool == null) {
 return null;
 }

 if (bool) {
 return "true";
 } else {
 return "false";
 }
 }

 ...
}

When this method is called from Kotlin code, both the argument and return type are treated as nullable types.

// this is fine
Utils.getBooleanAsString(true)?.capitalize()

// this is not
Utils.getBooleanAsString(true).capitalize()

For code that simply can’t be converted to Kotlin yet, this is not only a great way to indicate the intent of method arguments and return types, but also enforce them in Kotlin code. Your IDE may also show warnings in your Java code if it thinks you might pass or return a null object to a @NonNull annotated method.

Extension methods

Do you ever find yourself writing static utility class methods to add needed functionality to a class? Of course you do.

Here’s one we use for reconciling the fact that ViewTreeObserver.removeGlobalOnLayoutListener() was deprecated and renamed to ViewTreeObserver.removeOnGlobalLayoutListener() in Android SDK v16 (Jelly Bean).

import android.os.Build;
import android.view.View;
import android.view.ViewTreeObserver;

public class ViewUtils {

 public static void removeOnGlobalLayoutListener(View view, ViewTreeObserver.OnGlobalLayoutListener listener) {
 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
 //noinspection deprecation
 view.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
 } else {
 view.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
 }
 }

 ...
}

The call to this looks like

ViewUtils.removeOnGlobalLayoutListener(view, listener);

Straight-forward enough, but wouldn't it be so much nicer if we could just add this reconciliatory method right to the View class itself? With Kotlin extension methods, now you can (in a sense).

import android.os.Build
import android.view.View
import android.view.ViewTreeObserver

fun View.removeOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener) {
 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
 @Suppress("DEPRECATION")
 this.viewTreeObserver.removeGlobalOnLayoutListener(listener)
 } else {
 this.viewTreeObserver.removeOnGlobalLayoutListener(listener)
 }
}

This tells the compiler that, syntactically, we want this method to be treated as though it's part of the View class that we can't modify. With an import pointing to the extension method, we can now call this method like it was part of the View class itself. Notice also that Kotlin lets you define free-standing methods like this outside of a class.

import com.michiganlabs.app.extensions.removeOnGlobalLayoutListener

...

view.removeOnGlobalLayoutListener(listener)

Interoperability with Java

Kotlin's interoperability with Java is what makes a transition to using Kotlin in existing Android Java projects very smooth and relatively painless. We can build both Java and Kotlin code files in the same project, and with only a few additions to the project's Gradle build configuration file. Kotlin compiles down to Java byte code, so in the end, Kotlin is essentially syntactical sugar on top of the JVM. From Kotlin code, we can call Java code with almost no issues. From Java code, we can also call Kotlin code with occasional modifications, such as with the extension method in the above example. The Kotlin compiler generates a ViewKt class for us, with a static method that takes a `View` as its first argument in identical fashion to the static Java utility method we had earlier.

import com.michiganlabs.app.extensions.ViewKt;

...

ViewKt.removeOnGlobalLayoutListener(view, listener);

Going forward

Kotlin has already proven to be a significant improvement for us in Android development, from a decrease in bugs to the increased readability of code and the reduced development time. Delivering quality software in a timely manner is always a challenge, but with the many niceties of the Kotlin language, it's become just a bit easier and more efficient.

Joseph Kreiser
Joseph Kreiser
Software Developer

Looking for more like this?

Sign up for our monthly newsletter to receive helpful articles, case studies, and stories from our team.

How our Associates are using AI tools: Advice for early-career developers
Development

How our Associates are using AI tools: Advice for early-career developers

August 13, 2024

Our 2024 Associates at Michigan Labs share their experiences using AI tools like GitHub Copilot and ChatGPT in software development. They discuss how these tools have enhanced their productivity, the challenges they've faced, and provide advice for using AI effectively.

Read more
Advanced Tailwind: Container Queries
Development Web

Advanced Tailwind: Container Queries

July 28, 2023

Explore some advanced web layout techniques using Tailwind CSS framework

Read more
From bits to qubits: The future of quantum computing
Development

From bits to qubits: The future of quantum computing

July 10, 2024

Learn how quantum computing, which uses qubits capable of representing both 0 and 1 simultaneously, revolutionizes data processing. Discover the impact it could have on industries like finance and pharmaceuticals by enhancing risk assessment, fraud detection, and drug discovery.

Read more
View more articles