Whats new features in Kotlin 1.1.2 version

0
458
Standard Library API in Kotlin 1.1.2 version
Standard Library API in Kotlin 1.1.2 version

Recently Kotlin 1.1.2 version launched with some new features and resolves some bugs. I decided to research the upcoming changes released in past four EAP milestones and go through those that might impact Android Development.

Previously we discuss some disadvantage of Kotlin and what bugs resolved in Kotlin 1.1.2 version.

New Standard Library API in Kotlin 1.1.2 version

Here we introduce new functions and classes that are going to be published in Kotlin 1.1.2 version.

1takeIf() and also()

These are two general-purpose extension functions applicable to any receiver.

also is like apply: it takes the receiver, does some action on it, and returns that receiver.
The difference is that in the block inside apply the receiver is available as this,
while in the block inside also it’s available as it (and you can give it another name if you want).
This comes handy when you do not want to shadow this from the outer scope:

fun Block.copy() = Block().also { it.content = this.content }

takeIf is like filter for a single value. It checks whether the receiver meets the predicate, and
returns the receiver, if it does or null if it doesn’t.
Combined with an elvis-operator and early returns it allows to write constructs like:

val outDirFile = File(outputDir.path).takeIf { it.exists() } ?: return false
// do something with existing outDirFile

val index = input.indexOf(keyword).takeIf { it >= 0 } ?: error("keyword not found")
// do something with index of keyword in input string, given that it's found

2Coroutines

One of the hottest features introduced in the first milestone of version 1.1 and redesigned in next ones are coroutines. It’s a mechanism that provides ability to improve asynchronous programming in Kotlin. The aim is to eliminate a well-known callback hell with suspended computation.

Concurrency is a very important part of Android, because we often need to perform many operations at the same time and we constantly care about single threaded UI suffering from long-running operations. Do you remember AsyncTask? I hope you would prefer to forget about that class from Android API. Three type arguments and a lot of boilerplate code needed just to perform a simple background operation with a result published on the main thread.

Here is how long asynchronous operations can be executed with external kotlinx-coroutines-async library:

fun loadUser(id: Int) = async {
    val user = api.getUser(id).await() // not blocking main thread
    textView.text = "User: ${user.name}"
}

It is so clean, isn’t it?

What’s more, under the hood async and await are not reserved keywords (like in other languages such as C#). They are regular functions. For now, this methods are built upon CompletableFuture class and require minimum SDK version of Android app set to API level 24 as it uses Java 8 Language Features.

3groupingBy()

This API is to group a collection by key and fold each group in the same time. It consists of two parts: groupingBy function, and terminal operations — fold, reduce, eachCount.

First you invoke collection.groupingBy { key } function, which just returns a Grouping instance binding the provided key selector to the collection of elements. Then you call one of folding operations available for Grouping, which iterates the collection and populates the resulting map with the result of folding elements in each group.

For example, it can be used to count the frequencies of characters in a text:

val charFrequencies: Map<Char, Int> = text.groupingBy { it }.eachCount()

4minOf() and maxOf()

These functions can be used to find the lowest and greatest of two or three given values, where values are primitive numbers or Comparable objects. There is also an overload of each function that take an additional Comparator instance, if you want to compare objects that are not comparable themselves.

val list1 = listOf("a", "b")
val list2 = listOf("x", "y", "z")
val minSize = minOf(list1.size, list2.size)
val longestList = maxOf(list1, list2, compareBy { it.size })

5Type Aliases

Aliasing types was a long-awaited feature for Kotlin developers. The key benefit is a possibility to reduce repetitions and complexity of long type names. It applies mainly to function and generic types. In Android applications development it’s becoming very useful when defining common UI actions.

How many times have you needed to add or remove parameters in a lambda and then continue refactoring in different places?

As an example, suppose that you need to create a ViewHolder and pass a nullable lambda as an argument to the constructor to optionally easily listen for click events on the item view:

class ViewHolder(view: View, val action: ((Item) -> Unit)? = null) : RecyclerView.ViewHolder(view) {
    fun bind(item: Item) {
        itemView.setOnClickListener { action?.invoke(item) }
    }
}

6Map.getValue()

This extension on Map returns an existing value corresponding to the given key or throws an exception, mentioning which key was not found.
If the map was produced with withDefault, this function will return the default value instead of throwing an exception.

val map = mapOf("key" to 42)
// returns non-nullable Int value 42
val value: Int = map.getValue("key")
// throws NoSuchElementException
map.getValue("key2")

val mapWithDefault = map.withDefault { k -> k.length }
// returns 4
val value2 = mapWithDefault.getValue("key2")

7Map.minus() operator

In Kotlin 1.0 you could easily get a copy of a read-only Map with a new key-value pair inserted with the extension operator Map.plus(). However to remove a key from the map you have to resort to less straightforward ways to like Map.filter() or Map.filterKeys().
Now Map.minus() operator fills this gap. There are 4 overloads available: for removing a single key, a collection of keys, a sequence of keys and an array of keys.

val map = mapOf("key" to 42)
val emptyMap = map - "key"

8Inlining Property Accessors

In Kotlin 1.0 we could already create inline functions. Modifying property accessors with inline keyword might be considered as an abuse and quite strange for the first time. However, you should know that inlining is a powerful weapon against increasing methods count in DEX files, what in Android really matters.

This feature might be used in conjunction with another one (from Kotlin 1.1.2 version) that enables inferring property type from getter:

inline val Context.alarmManager 
    get() = getSystemService(Context.ALARM_SERVICE) as AlarmManager?

9Array-like List instantiation functions

Similar to the Array constructor, there are now functions that create List and MutableListinstances and initialize each element by calling a lambda:

List(size) { index -> element }
MutableList(size) { index -> element }

10String to number conversions

There is a bunch of new extensions on the String class to convert it to a number without throwing an exception on invalid number:
String.toIntOrNull(): Int?, String.toDoubleOrNull(): Double? etc.

val port = System.getenv("PORT")?.toIntOrNull() ?: 80

Note that these functions will box resulting numbers before returning them, since the return type is nullable, and nullable numbers are represented as boxed values.

Also integer conversion functions, like Int.toString(), String.toInt(), String.toIntOrNull(),
each got an overload with radix parameter, which allows to specify the base of conversion (2 to 36).

11Less Restrictive Inheritance

The next big changes concern inheritance in Kotlin. Firstly, until Kotlin 1.1 it was impossible to declare a subclass of a class with sealed modifier outside of the sealed class declaration. Now this limitation does not exist anymore (excluding non top-level classes). We are only limited to declare subclasses in the same file as sealed class like below:

package inheritance

sealed class S // no { here

class A : S()

class B : S() {
    // ...
}

Thereby subclasses do not need to have that complex names. You can import A class with import A instead of import S.A.

Incoming release unlocks also an inheritance for data classes. Yes, we can now inherit them from other non-final classes! According to that, now data classes can be also declared in sealed hierarchy.

12onEach()

onEach is a small, but useful extension function for collections and sequences, which allows to perform some action, possibly with side-effects, on each element of the collection/sequence in a chain of operations.
On iterables it behaves like forEach but also returns the iterable instance further. And on sequences it returns a wrapping sequence, which applies the given action lazily as the elements are being iterated.

inputDir.walk()
        .filter { it.isFile && it.name.endsWith(".txt") }
        .onEach { println("Moving $it to $outputDir") }
        .forEach { moveFile(it, File(outputDir, it.toRelativeString(inputDir))) }

13Map.toMap() and Map.toMutableMap()

These functions can be used for easy copying of maps:

class ImmutablePropertyBag(map: Map<String, Any>) {
    private val mapCopy = map.toMap()
}

14Destructuring and Underscores

Kotlin 1.1.2 version comes with many syntax cosmetics to eliminate any boilerplate code. Destructuring declarations in lambda parameters is one of that features. That eliminates such manual destructing from our code:

val (uuid, name, date) = it

Here is how unpacking a composite value passed to lambda looks like:

data class Report(val uuid: String, val name: String, val date: Long)

fun loadReport(uuid: String) {
    api.getReport(uuid) { (_, name, date) ->
        reportView.text = name
        dateView.text = formatDate(date)
    }
}

In the code above you could also spot another new language feature. It is an underscore character (_) used to remove unused parameters from destructing declaration. We are not using an uuid parameter so its name can be easily omitted.

With new release underscore characters can be also used in numeric literals to separate digit groups and improve readability of large numbers.

15Abstract collections

These abstract classes can be used as base classes when implementing Kotlin collection classes.
For implementing read-only collections there are AbstractCollection, AbstractList, AbstractSetand AbstractMap, and for mutable collections there are AbstractMutableCollection, AbstractMutableList, AbstractMutableSet and AbstractMutableMap.
On JVM these abstract mutable collections inherit most of their functionality from JDK’s abstract collections.

It is hard to discuss all the changes that will improve our productivity after switching to Kotlin 1.1.2 version but top 15 new features i discuss with you. Hope you like this article.

Share your thoughts

Loading Facebook Comments ...
Loading Disqus Comments ...