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
NullPointerException
s 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 NullPointerException
s 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.
Looking for more like this?
Sign up for our monthly newsletter to receive helpful articles, case studies, and stories from our team.
When to “shape up” versus be more “agile”
May 30, 2024Should you develop your new software using the Agile or Shape Up method? Here’s how to decide.
Read moreWhy Use Flutter?
January 18, 2023We discuss Flutter, a framework for building Android and iOS apps with a single codebase, created by Google in 2018. Given the increasing popularity of smartphones and mobile-first experiences, it is essential to cover not only Android and iOS platforms but also the mobile-first web.
Read moreFrom bits to qubits: The future of quantum computing
July 10, 2024Learn 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