10 Reasons Why You Should Drop Java and Switch to Kotlin

In the following article, I’m going to be listing 10 advantages Kotlin has over Java. Some common complaints I usually hear against learning Kotlin is that “Java already works fine” or “I already know Java and don’t want to learn another language”. The first reason is true to an extent, however the second is sheer laziness. Believe it or not, your language of choice will probably not be popular or around forever and even if it is, better ones may come along. As was the case with Kotlin being announced as an official language for Android development at Google IO 2017. On top of this, if you are already an intermediate or above in Java – learning Kotlin should not take you much time at all. If you are new to Android development I suggest reading another one of my articles, “How to Become an Android Developer” or this one if you want to see what Android development with Kotlin looks like.

Note: these are in no particular order

10. Model Objects

Unlike in Java, where, when creating classes only meant to hold data (models or POJOs to be specific), we are required to write all of the fields, setters, getters as well as a constructor. In Kotlin, we can define all of these things on a single line.

Java

Kotlin

Both of these classes are identical in functionality. As you can see the Java class is your standard POJO (Plain Old Java Object) with a constructor and setter/getter methods. The Kotlin code looks a little bit different (and much shorter), but it is the same class. In Kotlin, we include the constructor and class members when declaring the class itself. We also do not need to write explicit setter and getter methods, which is huge. Var is used to mark our fields as mutable, meaning we can change the class’ property data after declaring it. However, if we used val (immutable) it would be the equivalent of using the final keyword in Java.

You might be wondering why the lack of needing to write setters and getters is such a big deal, especially since any good IDE will generate them for you instantly. It essentially boils down to code maintainability. What if you wanted to add/remove a property or add some sort of validation code later on? You would need to come back and make a large amount of changes to the Java class, whereas the amount of changes needed in a Kotlin class would be minuscule.

Kotlin – no setters and getters?

In Kotlin, these are auto-generated, that means we do not need to write them ourselves (they are created under-the-hood). If we wanted to include custom set or get behaviour, we would declare it directly on the property like this:

We now omit this property from our constructor in this case, as it is now solely relying on the age and yearOfGraduation properties. We can also make a constructor in the traditional Java fashion.

Data classes

We can also make our Student class a data class. We only need to use the data keyword in the class declaration like so:

This will give us correct implementation for various functions such as toString(), copy(), equals(), hashcode() as well as the ability to deconstruct declarations.

9. Extension Functions

Extension functions are great – they completely remove the need for the various “Util” classes in Java. They allow us to declare functions on already existing types. Let’s look at the following example:

Instead of having spaceOut() take a String as a parameter, we can actually declare a receiver type. Essentially, this means that this function will be extending a type (adding a function to it). We can also make calls to the extending type directly within the function, thus removing the need for our single String parameter. We do this like so:

Notice the changes we have made both to how we call spaceOut() and the function code itself. Since we use String as the receiver type for spaceOut() – it is now a String function. That means we can call this function on any String we want.

In the function itself, we reference the String the function is being called on with the keyword this – as it is the immediate scope in our extension function. Doing this allows us to remove the single String parameter. Like normal functions, we can still have extensions which take one or more parameter.

Toast Example

One more example – let’s see how we can apply this to toasts. Normally, we would write a Toast like this:

However, with an extension functions we can shorten the amount of code drastically:

So, in a project with many toasts, all our calls would be shortened to only calling this extension and passing in a String:

8. Function Literals

Function literals are kind of a thing in Java. We write them as functional interfaces.

Java

We can then pass this interface as a lambda like so.
 

Kotlin

Unlike in Java, in Kotlin we can define this behaviour as a function literal.

Here we are defining a top-level (outsite of a class) function which takes two Ints and returns an Int. We then define it as:

A more accurate conversion of the Java example would look like this:

Now there is absolutely no need for an interface as we define the behaviour of the function directly as a parameter in someOperation().

7. Combining Extensions & Function Literals (Higher Order Functions)

We can take advantage of both extensions and function literals to achieve what is known as higher order functions (functions which take a function as an argument). Think of the concept of transactions – these usually require a various amount of method calls followed by some sort of “commit” function. Good examples of these would be database transactions, fragment transactions or editing   SharedPreferences.

Fragment Transaction Example
This is an extension function with FragmentManager as a receiver that takes a function literal as a paramater. The literal we pass in is a FragmentTransaction function which takes no parameters but returns a FragmentTransaction. If you take a look at the body of the function, we call beginTransaction() followed by the function we pass in ending with commit(). So, what exactly are we doing here? Essentially, we are cleaning up a lot of boilerplate that comes with running fragment transactions. Instead of calling beginTransaction() and commit() manually on every single transaction we make, we now only have to do that in this one function. Here’s what a working example of this looks like:
Here’s an abstract class for hosting a single fragment. In setupFragment() we are doing the normal amount of work for adding a fragment to our container, but notice where we call doTransaction() – this is our extension function from earlier. We are only passing in what we want our transaction to do. The extension is taking care of calling beginTransaction() and commit() so we don’t have to.

SharedPreferences Example

Let’s look at how we can apply the same concept with the SharedPreferences interface.

Notice the call chain we are doing in each set() function. This can be shortened with the following extension function:

Each set method in the previous example can now be shortened to the following:

Inline functions? What are those?

You might have noticed the use of the keyword inline in some of the previous examples. Normally, when Kotlin is decompiled to bytecode, if we do not mark a function, which takes a literal as a paramater, as inline – it is created as an anonymous class. Obviously, this creates memory overhead as we are creating a new object.  Look at the previous example for the extension doing fragment transactions. This is what the code looks like (when recompiled to Java) if we do not mark doTransaction() as inline.

No inline
Notice that when doTransaction() is called in this snippet, we are creating an anonymous class – Function1. Let’s see doTransaction() marked as inline now.

Inline
You can see that we no longer have an anonymous class. The code is being copied directly into setupFragment(). The benefit of marking higher order functions as inline should now be clear. Be aware however, that if you inline a very large function and use it everywhere – the size of your project will grow noticeably. When writing your own higher-order functions, you will need to analyze if the trade off for size vs. memory is worth it. More often than not, you will be marking every higher order function as inline though. Just be aware of the potential repercussions.

6. Null Safety

Kotlin as a whole has a much bigger emphasis on overall null safety. When it comes to variables in Java, everything is mutable by default (unless marked as final), but Kotlin gives us an option. There are two ways we can define our variables in Kotlin, either as immutable ( val) or immutable ( var). Once we initialize a val, it cannot be changed, however a var can be.

Furthermore, there exists a way to mark references as nullable or non-nullable. The following String cannot be null.

We can declare a reference that can be made null using ?.

Now, if we choose to, we can make s null. Since s can now be null, Kotlin will give us a compile error when calling a function on it.

This means that we must safely handle this call in case s is actually null at runtime. Kotlin gives us a few ways of handling that.

The !! Operator (throwing Null Pointer Exceptions)

If you feel like it would be a good idea to throw a NullPointerException (how could you…), you can use the !! operator.

If s is null when we attempt to get its length, we will be met with a NPE, crashing our program.

Using an if statement

The tried and true if statement works in Kotlin as well.

This approach is safer than the above, but we are still wrapping it in an if statement. Kotlin has more elegant solutions for dealing with potential null values.

Safe calls

Similar to the !! operator, we can use ? to make safe calls on our nullable types. Meaning, the call will only work if the reference is not null.

Now, we will get the length only if s is not null.

The ?: operator

The ?: or elvis operator, allows us to use another value if our nullable reference is indeed null. This works well as a replacement for if statements which only exist to check if a value is null. The example below should make sense.

If s is null, size will be equal to -1, otherwise we get the size of s.

 

5. Removal of findByViewId Boilerplate

With Kotlin skyrocketing in popularity, especially within the realm of Android development, it was only a matter of time until an Android specific plugin was made. In its non-experimental form, this plugin is used for view binding. Similar to Butterknife, but much less code. We’ll look at three examples of view related boilerplate code and how much easier this is now with the Android Kotlin plugin.

findByViewId

This is the original-vanilla-Android method for getting a reference to our views. Check the below example.

You can see that we are defining these views as class member properties so we can use them anywhere within the Fragment. We then grab a reference to each of them in onCreateView(). This example shows the use of only two views ( Button and TextView), but in a layout with many, the amount of boilerplate could grow to a ridiculous level.

Butterknife
Now that we are taking advantage of Butterknife, we no longer have to use the traditional findByViewId method. However, we do need to specify the id of every view in each of the @BindView annotations. This is a more elegant approach, compared to always calling findByViewId(), as we are able to immediately see which view ids belong to each respective view defined in our class.

Kotlin Android Extensions Plugin

As I initially mentioned at the beginning of this section, the Kotlin Android plugin completely removes the need for you to explicitly bind views to class member properties (this is taken care of under the hood). Instead, all you need to do now is use the explicit id of the view.

No more findByViewId or class member views. Pretty easy huh? The plugin generates classes which take care of view initialization and caching for you so you can focus on the more important stuff when developing your app.

4. The “when” expression

The when expression is the equivalent of  a switch statement in Java. Take the following code for instance.

Java

The above code can be shortened with a when statement in Kotlin.

Kotlin

What if we wanted to return each String instead of printing them? Kotlin makes this really easy for us. All we need to do is add the return keyword before our when.
The Java alternative looks like a complete nightmare in comparison – we need to add a return statement for every single case.

The when statement can also be used for if else blocks if we do not supply an argument.

Not to mention, you can also use when for if else like behaviour for checking the type or inheritance of an argument.

3. Default Arguments

Kotlin removes a lot of the need for overloaded methods with the introduction of default arguments. When writing a method in Java, we will sometimes have to define multiple methods to handle more than one parameter. Let’s take a look at LayoutInflater’s inflate() method.

Java

As you can see, the only purpose the second function serves is to call the first one. We can eliminate the need for this behaviour in Kotlin with default arguments.

Kotlin

This Kotlin code is doing the same thing the first two Java methods are with only one function. By default, attachToRoot will now always be false – when calling this function we do not have to provide a parameter for the 3rd argument unless we actually want to. If we do, we set the argument name to the desired value.
 

Default Argument Constructors

This behaviour also holds true for constructors.

Instead of being required to provide three arguments every time to create our object, we can do this instead:

Now we can create an animal object without providing any arguments to its constructors.

2. Delegation

Kotlin provides native support for the delegation pattern. Let’s see how we would first do it in Java.

Java

In order to provide the delegating behaviour (for TruckEngine), we need to create an instance of the engine implementation we want and then make sure to match each method call. If we wanted to override the behaviour, we could of course just go ahead and remove any call to RocketEngine.

Achieving this behaviour is much simpler in Kotlin – it’s not even one line!

Kotlin

In Kotlin, when implementing an interface we want to delegate to an existing implementation, we use the by keyword. No other code is required – our TruckEngine now behaves exactly like a RocketEngine. What if we wanted to override some of the behaviour though? All we need to do is override the function we want to change.
We can also provide a constructor to pass in any kind of Engine implementation as well.
 

1. Objects, Companions & Static

In Kotlin, the static keyword has been removed. This is was done as a way to add consistency to the design of the Kotlin language. Achieving static behaviour is now done in various ways.

Objects

Objects in Kotlin are the equivalent of singletons in Java. Like Java, they are not manually instantiated and only one instance exists at a time. We write them like this:

We also call an object like you would expect.
Decompiled

If you are curious, here is what the above object looks like when recompiled to Java (it’s exactly like a traditional singleton).

Companion Objects

Classes having static member properties or functions are also no longer a thing. In Kotlin, we are able to define what is known as a companion object. In our companion, we can place things we want to able to be accessed in a static context.

We call these functions like this.
You can also give your companion object a name if you wish.
Liked the article? Share it!

Leave a Reply

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
Notify of