Kotlin logo origami

Mastering Kotlin: A practical guide for Android devs

At this year’s I/O, Google made me happy with the announcement of Kotlin as a first-class language for Android development. Kotlin has been around for several years, and I prefer it over Java.

However, many developers have been wary about writing too many features for production code with Kotlin because it lacked official support from Google. Now that fear is gone, and many Android developers are jumping on the Kotlin bandwagon and starting to write production features in Kotlin.

Kotlin brings a lot of new things to the table. Here is a practical guide to help you quickly learn some of the language's fundamentals.

World Quality Report 2017-18: The state of QA and testing

Understanding “var” and “val” in Kotlin

Variables are the core concept that you need to grasp before doing anything else with Kotlin.

There are two ways in which you can define a variable: val and var. From the names, you might infer that “val” refers to “values” (which can not change) and “var” refers to “variables” (which can change).

Kotlin prefers the use of immutable values wherever possible, which makes the syntax less verbose than Java (where you need to use an extra final keyword to make variables immutable).
val message = "Hello"
var price = 100

You might notice that I didn’t use any type (such as int, long, String, etc.) for the variable declarations. The type is automatically inferred by Kotlin. This makes your code even more crisp and concise.

But you can always explicitly mention the types if you want to, like this:

val message: String = "Hello"
var price: Int = 100

Strictly speaking, there is no concept of types in Kotlin, not even void. Everything in Kotlin is an object, much like Ruby. Unlike Java, if nothing would be returned, then Kotlin will still return a Unit object. You can think of Unit as null/void in this instance.

Kotlin can also magically cast your objects, thanks to its powerful type inference system.

val message = "Hello Kotlin!"
if (messageis String) {
    println(0, message.length / 2))
}

While declaring the variable message, we haven’t specified that it is of type String. But it intelligently infers that there is no other type possible and casts it automatically to String, so you can use the methods available in the String class seamlessly.

Classes and properties in Kotlin

If you want to write an Android app in Kotlin, then classes are something you have to be familiar with.

Defining classes in Kotlin is super simple, as expected,

class Request

This is a perfectly valid, complete class declaration in Kotlin. You don’t even need the braces if your class doesn’t have any extra code. You will obviously want to write some constructors for your class. In Kotlin, you can have one primary and multiple secondary constructors (if needed).

class Request(val requestId : String)

You can easily define the primary constructor by inlining it with the class definition. This is an example of how clean and concise the entire syntax looks.

I have defined a class named “Request” having an immutable property (more on this later) named “requestId” of type String and have also defined a constructor to assign values to it—all of this in just a single line of code.

If you need to use some kind of annotations in your constructor (such as for Dependency Injection using Dagger 2), you can use the optional “constructor” keyword.

class Request @Inject constructor(val requestId : String)

Now let’s have a look at the concept of properties in Kotlin.

You can think of properties as a combination of what we know as fields and getters/setters in Java, together as a bundle.

class Customer {
  var name = "John"
  var phoneNumber = "1234567890"
}

Here, the name is basically a property of the class Customer. It has an internal backing field and a getter and a setter method already associated with it. Just imagine the amount of code it can automatically reduce for you.

class Customer {
  var name = "John"
    get() = name.toUpperCase();
    set(value) {
      field = value.toUpperCase()
    }
  ...
}

You can always use custom getters and setters if you need to (just as you always did in Java). You might be surprised by the “field” variable mentioned in the setter method, but don’t worry; it is nothing but the internal backing field I talked about earlier.

In this sample, whenever you try to use the getter, you will always get the name returned in upper case, and the setter lets you convert the given string into uppercase before assigning it to its corresponding backing field.

Make the most of the extension functions in Kotlin

There are times when you want to make use of a particular functionality at several places in your code and you end up writing wrapper or utility classes with static methods to make that possible.

But Kotlin lets you add extra functionality to any class you want, even to the those you have no control over (such as Android Framework classes or core Java classes), with the help of a super-cool feature called Extension Functions.

Extension functions, as the name suggests, are just like ordinary functions that let you extend extra capabilities to any existing class without touching the internal code of that class in any way. Sound cool? 

Let’s define a very simple extension function first:

fun View.makeVisible() {
  this.visibility = View.VISIBLE
}

And now let’s put it to use:

view.makeVisible()

Here, we are defining an extension function of the View class (which belongs to the Android Framework) so that we can use it just as if it were a method belonging to the class itself. We don’t have access to the View class yet, but we could extend its functionality and add methods to it using the Extension Functions.

The simplicity of Kotlin's data classes

Data classes are normal Kotlin classes (as I have discussed before) on steroids. Data classes should only maintain state or data and should not be responsible for performing any operation or functionality.

Data classes are extremely simple to write, since Kotlin does the heavy lifting of generating a ton of code for you automatically under the hood. It can give you the getters/setters, equals() and hashCode(), copy() method, etc., all for free.

A typical Customer model class will look something like this in Java:

public class Customer {

  private int id;
  private String name;
  private String address;

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

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

  public String getAddress() {
    return address;
  }

  public void setAddress(String address) {
    this.address = address;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Customer customer = (Customer) o;

    if (id != customer.id) return false;
    if (name != null ? !name.equals(customer.name) : customer.name != null) return false;
    return address != null ? address.equals(customer.address) : customer.address == null;
  }

  @Override
  public int hashCode() {
    int result = id;
    result = 31 * result + (name != null ? name.hashCode() : 0);
    result = 31 * result + (address != null ? address.hashCode() : 0);
    return result;
  }

  @Override
  public String toString() {
  return "Customer{" +
      "id=" + id +
      ", name='" + name + '\'' +
      ", address='" + address + '\'' +
      '}';
  }
}

The equivalent code for Kotlin (with some extra features) looks like this: 

data class Customer(var id: Int, var name: String, var address: String)

So 65 lines of code in Java get reduced to just a single line of code in Kotlin. If you add, remove, or modify any property in these data classes, all the auto-generated code gets automatically updated as well, simplifying what was always a painful and tedious job in Java.

Moreover, because we should always favor immutability wherever we can, we can easily transform our old data to a new immutable data class just by replacing all the vars with vals.

data class Customer(val id: Int, val name: String, val address: String)

Now suppose our fictitious customer gets relocated to a different city and his address needs to be changed. We cannot do that in this object because it is immutable and cannot be changed once created.

val customer1 = Customer(100, "John Rogers", "New York")
val customer2 = customer1.copy(address = "Las Vegas")

The nifty auto-generated copy method helps us create a copy of the existing customer object while allowing us to change the address. We could also change multiple properties all at once, if we wanted to:

val customer1 = Customer(100, "John Rogers", "New York")
val customer2 = customer1.copy(id = 101, address = "Las Vegas")

Getting rid of all findViewById()’s

If you are an Android developer, then you should already be quite familiar with the findViewById() method, since it is something that you need to use almost every day to reference views in code. But it is really boring to repeat the same old thing, again and again, every day.

You might have already gone for some alternatives such as using the popular ButterKnife library, which is really good at what it does, but Kotlin has always got something better to surprise you with.

All you need to do is apply the Kotlin Extensions plugin (apply plugin: ‘kotlin-android-extensions’) in your build.gradle file and you are good to go.

Now if you have an XML layout with a Button like this:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <Button
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Submit" />

</RelativeLayout>

And you want to access the button on your Activity and change its text:

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

  button.text = "Submit Now"
}

You basically don’t need to do anything extra at all to access the button from its XML definition. The plugin automatically generates some caching functions under the hood and makes view referencing super simple for you.

Write better code with lambdas

Lambdas are really powerful, since they can let you pass functionality as an argument to a method, making the code much more clear, concise, and compact.

Kotlin has built-in support for lambdas, which opens up a lot of programming possibilities. Suppose you want to set a click listener to a button. This is how your code would normally look:

button.setOnClickListener(object : View.OnClickListener {
  override fun onClick(view: View) {
    performAction()
  }
})

If you look closely, there is not much difference between that and what you would have written in Java. But the face of this code can be completely changed by using a lambda:

button.setOnClickListener({ view -> performAction() })

It takes a View as a parameter and returns a Unit (which is equivalent to a void in Java) object. Now, if the parameter is not used inside the lambda, the code can become even simpler:

button.setOnClickListener({ performAction() })

If the only parameter of the method is the lambda, then you can even omit the parenthesis completely:

button.setOnClickListener { doSomething() }

Just look at the transformation of the code. We have reduced five lines of boring, verbose, and error-prone code into a single, readable, and easily understandable line of code. 

Lambdas are great, but a certain level of runtime overhead is associated with them. That can be easily solved, though, by the inline functions available in Kotlin. Take a look at this page for a detailed understanding of when and how you should use inline functions.

Kotlin: A concise Java alternative

With a clear understanding of the core concepts of this language, you are ready to get started with it. It can be so much simpler to use than Java that you will probably want to master it quickly and start using it in your day-to-day Android development.

Image credit: Flickr

World Quality Report 2017-18: The state of QA and testing

 

Topics: Mobile