The Complete Kotlin Programming Language Review

The Complete Kotlin Programming Language Review

When you work with something for a long time, at some point you realize you need something new, like a gulp of fresh air. Otherwise, your favorite job can seem a little less favorite, even depressing to a certain extent. In software development at moments like these it is vital to have in reserve something that can be studied apart from or in addition to your main technology or programming language.

This is what Kotlin became for the Android community. Kotlin is a relatively new open source programming language that allows creating Android apps and was initially designed for the Java Virtual Machine. The dissatisfaction with Java 6/7 (used in Android development) has been accumulating for a long time now and even partial support of Java 8 couldn’t make things better – being too incomplete. This is when Kotlin comes on stage and JetBrains starts actively advertising it as a programming language for Android.

At first glance it all seemed suspicious, but we all know what happened at Google IO 2017 – Kotlin was given green light by the Google themselves, along with the fullest support for Kotlin and approval.

The advanced Android community sighed with relief – from now on it was possible to make all current and new projects in the Kotlin programming language without looking back at Java and with no fear that Google will interfere. I decided to learn Kotlin and review it here from the development perspective.

So what are the benefits of Kotlin vs Java and why should your next project be implemented specifically using Kotlin? Here’s my list of advantages:

  • as I mentioned earlier – Google announced their full support for this open source programming language and approved Kotlin for Android development, which means that all optimization/compatibility things that JetBrains did before, they now do in collaboration with the Google team
  • yes, it is possible to use Java 8 for Android, which is awesome, but in fact Java 8 is far less “tasty” than Kotlin, IMHO
  • Kotlin and Java are 100% compatible: you can combine the existing Java code with Kotlin code, use Kotlin classes in the Java code and vice versa.
  • Besides, again, Kotlin 1.1 supports Java 8
  • Kotlin is a great fit for server-side development
  • OOP in this programming language is just so richer than in Java
  • Null safety is implemented on the level of type system
  • you can use the best practices of functional programming (higher-order functions, lambdas, anonymous functions, function references)
  • Kotlin allows smart casts
  • Kotlin has extension functions
  • Android Studio provides excellent support for Kotlin. You can freely use all of the existing tools in Android Studio with your Kotlin code.
  • we don’t need ButterKnife anymore, since we have Android extension plugin
  • less code is required to receive actual results, in the end you’ll receive a concise easily readable code
  • you are tired of the helper classes with a set of static methods and you feel like there’s a smarter way to do it
  • you want to create business logic, but not just an infrastructure for it
  • now you can look at the problem/task from a different angle

Let us look into some of the points in more detail, we’ll review both Kotlin and Java, their pros and cons and best practices, so that you can understand whether you want to give it a try and learn Kotlin. Or maybe even consider fully switching from Java to Kotlin.

Table of contents:

Object Oriented Programming

I would recommend to start getting to know Kotlin not with “syntax sugar” and functional programming (FP) aspects, but with the best practices of Object Oriented Programming.

Once you look through this section’s documentation, you’ll see how well the developers at JetBrains have thought out the syntax and possibilities of the language. Everything is literally filled with love and care for us, people who’s going to use it every single day.

Things that require an unforgivable amount of time and effort in Java, in the Kotlin programming language are implemented with a few lines of code.

For instance, let’s take an ordinary Java data-class:

public class Message {
    private String text;
    private boolean isRead;

    public Message(String text, boolean isRead) {
        this.text = text;
        this.isRead = isRead;
    }

    public String getText() {
        return text;
    }

    public boolean isRead() {
        return isRead;
    }
}

And now let’s look at how it’s done in the Kotlin programming language:

class Message(val text: String, val isRead: Boolean)

One line in Kotlin against 14 in Java, impressive, right? Here’s a shortlist of Kotlin benefits in terms of Object Oriented Programming:

  • to create an instance class we don’t need the keyword “new”
  • no “extends” or “implements”, instead – “:”
  • a class can have only one primary constructor and one/a few of secondary constructors
  • a primary constructor doesn’t have a body, but you can define the initialization block “init” inside of it
  • the keywords var (mutable property)/val (read-only property) are used for properties declaration and initialization right in the declaration of the constructor. The keywords val/var are used also during the declaration of the ordinary local variables
  • there are also named arguments and you can set default values for them
  • you can define custom accessors for the properties
  • there’s no need now to explicitly call getter/setter to obtain/assign the property value – just use the name of the property itself!
  • by default the visibility area for Kotlin classes/methods/properties – public
  • by default all Kotlin classes are final and you need to use open-annotation for a class if another behavior is required – you’ll need to get used to this
  • the interfaces can now implement methods! Meaning that a class can implement 2 interfaces that have different method implementation with the same signature and you are now able to choose where to use which method
  • the generics variation is implemented by means of in/out keywords
  • anonymous inner classes can be implemented through object expressions & object declarations
  • our favorite singleton is easily done by object declaration approach
  • Java static methods = object declaration + companion object
  • the delegation is implemented on the class level using by-clause, the compiler will generate the necessary duplicating methods on its own

Usually this list is enough to evoke a burning desire to try all that. But before moving forward, let’s set a little task for ourselves and implement it using Java. In the end of the post we’ll try to accomplish the same in another programming language for Android apps – Kotlin.

So, we have a Message data class and we need to enable selection of the read messages and selection of messages from the same sender:

//Java code public class Message { private String text; private boolean isRead; private String author; public Message(String text, boolean isRead, String author) { this.text = text; this.isRead = isRead; this.author = author; } public String getText() { return text; } public boolean isRead() { return isRead; } public String getAuthor() { return author; } } // public class MessageHelper { //getting read messages static public List<Message> getReadMessages(List<Message> messages) { List<Message> result = new ArrayList<>(); for (Message message: messages) { if (message.isRead()) { result.add(message); } } return result; } //getting messages by specific author static public List<Message> getMessagesByAuthor(List<Message> messages, String author) { List<Message> result = new ArrayList<>() for (Message message: messages) { if (!TextUtils.isEmpty(message.getAuthor()) && message.getAuthor().equals(author)) { result.add(message); } } return result; } } //usage MessageHelper.getReadMessages(messages); MessageHelper.getMessagesByAuthor(messages, "Roger");

This sad situation should be familiar, but let’s try and fix it.

Null Safety

Comapred to Java, the variable in Kotlin cannot contain null if a compiler doesn’t know about it. In fact, while declaring the variable you have to specifically indicate whether it can be nullable or not. Everything is done to avoid even the slightest possibility of Null Pointer Exception (NPE) in the code and during runtime in particular. However still, the NPE may appear in the following cases:

  • the explicit call of throw NullPointerException()
  • an exception in the outer Java code
  • the usage of a specific operator !!

For example, let’s review the following code:

var strNotNull : String = "Hello" strNotNull = null //compilation error

In the second line we’ll get an error during the compilation attempt. In order to be able to set null into the variable, we need to do the following:

var strNull : String? = "Hello"

Now it’s going to work. By the way, the awareness of the fact that null can NEVER be found in the variable frees us from all double checks and we can easily do this:

val length = strNotNull.length

But what should you do if null cannot be added to the variable? Just use a safe-call operator — ?.

val length = strNull?.length

In this case the variable length will contain either the length of the string, or null, depending on the strNull value. The type of the variable will be set automatically to Int?. If we don’t wish to return null, we can use Elvis operator:

val length = strNull?.length ?: -1 //length is instance of Int, but not Int?

You can build the whole chain of such safe-calls, for example:

message?.sender?.birthdate?.year

If we need to execute a set of operations for the case when the variable does not equal null, we can use the let function:

//say good-bye to if (message != null) {} message ?.let { .. }

A perfect alternative to the usual if/else blocks.

Smart casts & type checks

Now you can forget about the code blocks like this:

if (obj instanceOf String) len = ((String) obj).length();

Because the guys from JetBrains have implemented a system of smart casts. The corresponding Kotlin code looks in the following way:

if (obj is String) len = obj.length

Now let’s imagine that we need to execute a few variable methods and let’s see how to do it nicely with the help of with function

with(obj) { val oldLen = length // what happens here is obj.length() is being called val newLen = replace(" ", "").length //and obj.replace(“ “, “”).length() here }

Ranges & Controlling Flow

Regarding the logical structures, their main peculiarity in Kotlin is that they can be used as expressions. For example, there’s a code in Java:

if (a > b) { max = a; } else { max = b; }

and here’s how it looks in Kotlin:

val max = if (a > b) a else b

If you need to execute more code in the condition blocks – no problems with that, but the last line must return the result into the variable. The same principle works for the when expression. When is the same as switch from Java and other C-like languages. However, when gives a considerable deal more freedom and usage options.

It was not accidentally that I combined ranges and flow control into one section. You cannot speak about the condition blocks and cycles without not mentioning ranges, since ranges are a wonderful addition that will make your life easier. Frankly speaking, I was very fond of ranges from the times of Pascal and considered them to be a very convenient solution and couldn’t comprehend why they are missing as a separate type in C/C++, PHP, JS and, of course, Java.

In Kotlin though I met them once again, complemented with wider and more convenient use options in comparison with Pascal. For example, range can be saved into the variable and used in the condition block:

val myRange = 1..10 if (x in myRange) { //you might want to use !in to check if value IS NOT in range ... }

1 and 10 can easily be substituted with variables or function call. While using them in cycle for you can specify the direction of movement and step size.

for (i in 1..4 step 2) print(i) // prints "13" for (i in 4 downTo 1 step 2) print(i) // prints "42"

Below I give a piece of code where I tried to collect all possible usages of when:

//just a helper function that checks whether int value is even or not? fun Int?.isEven() : Boolean = this ?.mod(2) == 0 val range = 10..20; //function with when-construction that takes a parameter of Any kind fun test1(x: Any? ) { when(x) { //here we pass in x to when null -> print("x is null") 0, 1, 2 -> print("x = $x") is CharRange -> print("x is a CharRange $x") in range -> print("x is in range $range, x = $x") is String -> print("x is a String, x = \"$x\"") else -> print("x is of ${x?.javaClass}, x = $x") } print("\n") } //function with when-construction that does not take a parameter and may substitute if/else-if blocks completely fun test2(x: Any? ) { when { //no value is passed in x == null -> print("x is null") x in 0..2 -> print("x = $x") x is CharRange -> print("x is a CharRange $x") x in range -> print("x is in range $range, x = $x") (x is Int? && x?.isEven() ? : false) -> print("x is even integer, x = $x") x is String -> print("x is a String, x = \"$x\"") else -> print("x is of ${x?.javaClass}, x = $x") } print("\n") } fun main(args: Array) { test1(null) // prints "x is null" test1(2) // prints "x = 2" test1('A'..'Z') // prints "x is a CharRange A..Z" test1(15) // prints "x is in range 10..20, x = 15" test2(22) // prints "x is even integer, x = 22" test1("HELLO!") // prints "x is a String, x = "HELLO!"" test1(101) // prints "x is of class java.lang.Integer, x = 101" }

Coroutines

The coroutines support appeared in the version 1.1. Coroutines are not included into the language itself at the moment, but they are an experimental functionality and are singled out into a separate artifact. In order to use them we need to write the following in the build.gradle file:

kotlin { experimental { coroutines‘ enable’ } } dependencies {… compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:"… } repositories { maven { url "https://kotlin.bintray.com/kotlinx/" } }

What is it then? In a certain sense, it’s a lightweight thread, but at the same time very cheap in servicing, which can also be paused and restarted. It can consist of the calls of other coroutines, which may be running in different threads. One thread can serve multiple coroutines. Kotlin provides a few mechanisms of coroutines usage, but I will review only a pair of the most interesting in the context of Android development. The first option is the launch() function:

launch(CommonPool) { ... }

CommonPool is a pool of repeatedly used threads and uses java.util.concurrent.ForkJoinPool under the hood. Below are two versions of the same solution: blocking calls (never do it!) and non-blocking ones based on the couroutines.

//this is a blocking example println("Start") for (i in 1..100) { //our main thread sleeps for 10 seconds...not good!!! Thread.sleep(100) } println("Hello!") println("Stop")

And in the console the result of the execution is the following:
11:22:41.250 419-419/com.test I/System.out: Start
11:22:51.269 419-419/com.test I/System.out: Hello!
11:22:51.269 419-419/com.test I/System.out: Stop

Here’s a non-blocking call:

//this ain’t a blocking example println("Start") launch(CommonPool) { for (i in 1..100) { delay(100) //this is coroutine specific call } println("Hello!") } println("Stop")

In this case the result of the execution will look like this:
11:27:32.824 6212-6212/com.test I/System.out: Start
11:27:32.838 6212-6212/com.test I/System.out: Stop
11:27:42.884 6212-6238/com.test I/System.out: Hello!

If we need to return the results of the coroutine work into the UI then we’d better use the async() function. It is very similar to launch(), the only difference is that it returns the result of operation as Deferred instance, which has the await() method and this one does return the function work result. For instance, we have the most primitive function for the factorial calculation.

fun factorial(num: Int): Deferred { return async (CommonPool) { var f: Long = 1 for (i in 2..num) { f *= i delay(100) //this is just for the purposes of an experiment } f //it's not a typo, it's a 'return' statement! } }

Inside the function the async-coroutine is launched, which will return the of the Long type value. I have set a delay in the cycle to show one curious detail of the coroutines work (we’ll talk about it in a bit). Now let’s perform a call of this function to see how it works:

println("Start") launch(CommonPool) { val f5 = factorial(5) val f25 = factorial(25) println((f5.await() + f25.await())) } println("Stop")

The work results are the following:
11:42:35.050 26517-26517/? I/System.out: Start
11:42:35.061 26517-26517/? I/System.out: Stop
11:42:37.486 26517-26530/com.test I/System.out: 7034535277573963896

And now let’s discuss things I wanted to draw your attention to. If you noticed, the difference between the last two outputs in the console is approximately 2.5 seconds. While 5! is calculated within ~0.5 seconds. And 25! is calculated during 2.5 seconds. Thus the println() is not going to be executed until all coroutines will have done their work, meaning – until the longest of them will have finished. Great, we’ve figured that out and now can actually come back to the previous task: how to correctly send the results of the coroutine work into the UI-thread. And here the module kotlinx-coroutines-android comes in handy, representing the UI context for the coroutines – a replacement for the CommonPool, used in the examples above. And what we need to do here is to add this module to the dependencies:

dependencies {… compile "org.jetbrains.kotlinx:kotlinx-coroutines-android:" }

Now we can safely do this:

println("Start") launch(UI) { val f5 = factorial(5) val f25 = factorial(25) myTextView.text = (f5.await() + f25.await()).toString() } println("Stop")

And some more interesting use cases:

//fetching data for list/recyclerview launch(UI) { try { val call = MyHttpClient.getLatestItems() //some API call with async() inside itemsAdapter.setData(call.await()) itemsAdapter.notifyDataSetChanged() } catch (exc: CustomException) { //logging exception } } //fetching and processing images launch(UI) { try { val imageCall1 = MyHttpClient.fetchImageByUrl(url1) val imageCall2 = MyHttpClient.fetchImageByUrl(url2) val resultImageFun = combineImages(imageCall1.await(), imageCall2.await()) //also an async() function imageView.setImageBitmap(resultImageFun.await()) } catch (exc: CustomException) { //logging exception } }

Functions

Kotlin provides an interesting set of tools to extend the simple term “function”. Now we can use all that stuff JS developers talk about and even more.

Local/Extension/Infix-functions

In software development we quite often encounter situations when one and the same piece of code inside one function needs to be executed several times, but in different parts of it. Yes, you could just do the Extract Method, but it will not be the desired solution. Why elevate the inner logic of one function (which does not occur anywhere else) to the class level? Kotlin has the Local functions that might come in handy. Now you can declare inner(local) functions inside other functions and they will have access to the variables declared in the external context.

If by any chance you decide to extend the functionality of an existing and not final class with your own set of methods, you will need Extension functions to accomplish it. My first desire was to go and extend the String class with my is Empty() method, in order not to use the external call TextUtils.isEmpty(). It turned out that I was late and the developers of Kotlin had already thought about it and added a String.isNullOrEmpty() method which does exactly what I needed.

Now that we have Extension functions, there’s no need to create additional static helper-classes. Let’s try and figure out how it works. For instance, you always wanted to know the length of a circle for an arbitrary integer if it were the radius of this circle. In the world of Java developers, you’d need to create a Circle class, for instance, and the method getLength() in it, or a static class with a separate method. Not too nice and quite cumbersome. Now let’s see how Kotlin does it:

fun Int?.circleLength() = if (this != null) 2 * this * PI else 0; println(10.circleLength()) // prints 62.83185307179586 println(null.circleLength()) // prints 0

In the Android context I can provide the following simple example of Extension functions usage:

//this is how we inflate layout to view in Java LayoutInflater inflater = LayoutInflater.from(getContext()); view = inflater.inflate(R.layout.item_user, container, false); //and this is how we can do it in a smarter way in Kotlin //just by extending ViewGoup class with ‘inflate’ function fun ViewGroup.inflate(layoutId: Int, attachToRoot: Boolean = false): View { return LayoutInflater.from(context).inflate(layoutId, this, attachToRoot) } view = viewGroup.inflate(R.layout.item_user, false)

If you wish to turn your function into an operation in terms of syntax, you can do it with the help of the key infix:

infix fun String.mix() = {...} //and now you can use your function in both ways //just as usual OR as a command "Hello " mix "World" "Hello ".mix("World")

HOFs/Anonymous functions/Lamdas

Kotlin wouldn’t enter the world of Android development in such a bold fashion if apart from everything mentioned above (although it’s already more than enough, imho) it didn’t offer truly new benefits in comparison with Java 6/7 (the support of Java 8 has only recently been added and in a rather simplified form). And it did offer the following things:

  • Higher Order functions(HOFs)
  • Anonymous functions
  • Lambdas

They are all tightly interconnected and in fact you can’t use HOF without using lambdas or anonymous functions. For those not familiar with the basics: HOF allow to accept other functions as parameters or return the functions as work result.

JS-developers use these things every other day, while doing the AJAX-request and transmitting anonymous functions as callbacks. In the very Python this has been implemented initially and is known by the term “decorators”. It is simple and convenient and not accessible to every Android developer.

If you have a function ready-and-waiting and you want to send it as a parameter you can do it by placing ‘::’ before its name. Moreover, in Kotlin 1.1 they added the ability to transfer the methods of a class instance in the same way. Let’s look into a simple example to see how it works. The task is the following: we have a list of lines and we need to select only those with even length. Our extension function— isEven() will help here.

fun Int?.isEven() : Boolean = this ?.rem(2) == 0 //just a dummy func used for an experiment fun check(str: String): Boolean = str.length.isEven() //@param validator - it’s a function that takes String param and returns Boolean value fun sortOutStrings(list: List, validator: (String) -> Boolean): List { val result = arrayListOf() for (item in list) if (validator(item)) result.add(item) return result }

//sending reference of our check() function inside sortOutStrings()

println(sortOutStrings(listOf("A", "AB", "ABC"), ::check)) //prints [AB]

Similarly, the filter() function in Collections receives lambda/function( the result of which is Boolean) to filter out the data and return the result collection.
In fact, this example still looks cumbersome, let’s try and simplify it using an anonymous function and a lambda:
//anonymous function

println(sortOutStrings(listOf("A", "AB", "ABC"), fun(str) = str.length.isEven())) //prints [AB]

//lambda with the chain of other functions that process the result of each other

println(listOf("abc", "ab", "a").filter{!it.length.isEven()}.sortBy{it}.map{it.toUpperCase})//prints [A, ABC]

//lambdas

println(sortOutStrings(listOf("A", "AB", "ABC"), {it.length.isEven()})) //prints [AB] println(sortOutStrings(listOf("A", "AB", "ABC")){it.length.isEven()}) //prints [AB]

The last two lines of code are especially interesting – in fact it is the same structure, but the syntax is slightly different. It is the implicit name of a single parameter. If the last parameter in the call is a function, then its body can be defined outside of parentheses, immediately after them – in the curly brackets. And that’s why it’s great: in this way you can define blocks consisting of several functions, and this is implemented using the function literal with a specified receiver object. On the one hand, this is very similar to the extension functions, because we can call the methods of this object without any additional qualifiers, only now we can pass the set of methods of this object that we want to call. Consider an example with TextView:

inline fun textview(parent: ViewGroup, setup: TextView.() -> Unit): TextView { val view = TextView(parent.context) view.setup() parent.addView(view) return view }

The textview() function takes a setup lambda as the last parameter with the explicitly specified type of receiver object – TextView, and you can use it like this:

textview(container) { layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) setText(R.string.app_name) // OR text = getString(R.string.app_name) setTextColor(Color.RED) textSize = 12 f; setOnClickListener { Toast.makeText(context, "That's Me!", Toast.LENGTH_LONG).show() } }

It means that all the code that is inside the parentheses will be executed in the place where the call to view.setup() occurs. This approach is very convenient for implementing builders and tree structures. If you liked this approach, I advise you to look for a separate section in the documentation on Type-Safe Builders, there is an excellent example of building HTML-markup using the approach of function with receiver object.

In the description of the textview() function there is an inline keyword. It tells the compiler that the code for this function (ie its body) needs to be inserted directly into the place where this function is triggered in its pure form. Thus, we save memory (cause we omit creation of one more abstraction), we do not lose in performance, BUT the number of output code grows slightly. It should not be forgotten and, if possible, you should use inline as much as possible and take all benefit out of it. For example, extension functions for base types are implemented in this very approach.

Going back to lambdams, it is worth noting that in Kotlin 1.1 they have added destructuring support and the ability to skip unnecessary parameters with the underscore “_”.

//this is our distracted data class data class DistractedDataClass(val id: Int, val text: String, val weight: Int) //some threshold value we want filter data against val threshold = 6; val dummyList = listOf(DistractedDataClass(0, "Hello", 1), DistractedDataClass(1, "World", 2)) //we don’t need first parameter for inner logic of lambda, so missing it in lambda declaration val goodList = dummyList.filter { (_, txt, w) -> txt.length + w > threshold } if (goodList.size > 0) println("${goodList.get(0).toString()}") //prints “DistractedDataClass(id=1, text=World, weight=2)”

And here goes the summary

Getting back to the beginning of this article where I set the simple task for the selection of read messages and messages for the chosen sender for the Message data-class, and let’s implement the same functionality using Kotlin means and tools:

// Kotlin code for data-class data class Message(val text: String, val isRead: Boolean, val author: String?) //usage messages.filter {it.isRead} messages.filter {it.author == "Roger"}

If you compare the number of useful lines of code in Java vs Kotlin, you’ll have 30 vs 3, the results are obviously not in Java’s favor.

Thus: if you are a seasoned (and not only) Android developer and you feel that it’s time for a change, but you don’t want to replace Android development with anything else, now it is the perfect time to use Kotlin for some new project. Trying new programming languages is the very gulp of fresh air which will not only add some variety into your routine coding but also will make it more interesting and challengeable.

Useful sources:
· Kotlin reference.
· Kotlin online.
· IDEA & Kotlin.
· Getting started with Android and Kotlin.
· Walkthrough tutorials.
· Android Development with Kotlin — Jake Wharton.
· Android Coroutines.