Kotlin has a fixed number or symbolic operators we can easily use on any class. The way is to create a function with a reserved name that will be mapped to the symbol. Overloading these operators will increment code readiness and simplicity.

Operators tables

Here you can see a set of tables with the operator and the corresponding function that must be implemented to implement the operator in a specific class

Unary operations

+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()
a++ a.inc()
a– a.dec()

Binary operations

a + b a.plus(b)
a – b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.mod(b)
a..b a.rangeTo(b)
a in b b.contains(a)
a !in b !b.contains(a)
a += b a.plusAssign(b)
a -= b a.minusAssign(b)
a *= b a.timesAssign(b)
a /= b a.divAssign(b)
a %= b a.modAssign(b)

Array-like operations

a[i] a.get(i)
a[i, j] a.get(i, j)
a[i_1, …, i_n] a.get(i_1, …, i_n)
a[i] = b a.set(i, b)
a[i, j] = b a.set(i, j, b)
a[i_1, …, i_n] = b a.set(i_1, …, i_n, b)

Equals operation

a == b a?.equals(b) ?: b === null
a != b !(a?.equals(b) ?: b === null)

The equals operations are a bit different, because they use a more complex translation in order to make a proper equals checking, and because they expect an exact function specification, and not just an specific name. The function must be implemented exactly like this:

fun equals(other: Any?): Boolean

Function invocation

a(i) a.invoke(i)
a(i, j) a.invoke(i, j)
a(i_1, …, i_n) a.invoke(i_1, …, i_n)

The example

As you can imagine, Kotlin lists have the array-like operations implemented, so we can access to list items the same way we’d do if it was an array in Java. But it goes beyond, in mutable lists the item can also be set directly in a very simple way:

val x = myList[2]
myList[2] = 4

In the book I’m writing, I’m creating a forecast app, and I’m making use of a data class called ForecastList:

data class ForecastList(val city: String, val country: String, 
                        val dailyForecast: List<Forecast>)

This class is basically a list with some extra info. It’d be interesting to access its items directly instead of having to access its internal list to get an item. Besides, as I’m using it in a recycler adapter, the creation of a size() function will help too:

data class ForecastList(val city: String, val country: String, 
                        val dailyForecast: List<Forecast>) {
    public fun get(position: Int): Forecast = dailyForecast[position]
    public fun size(): Int = dailyForecast.size()
}

It makes our onBindViewHolder a bit simpler. Before I was using:

val forecast = weekForecast.dailyForecast[position]

And now I can do:

val forecast = weekForecast[position]

The same for the getItemCount() function, though it hasn’t much to do with operator overloading:

override fun getItemCount(): Int = weekForecast.size()

2.3 Operators in extension functions

We don’t need to stick to our own classes, but we could even extend existing classes using extension functions to provide new operations to third party libraries. For instance, we could access to ViewGroup views the same way we do with lists:

public fun ViewGroup.get(position: Int): View = getChildAt(position)

Now it’s really simple to get a view from a ViewGroup using the position:

val container: ViewGroup = find(R.id.container)
val view = container[2]

Conclusion

With this operator overload, we can really simplify our code and make it more readable, by using the standard symbols to perform familiar operations over any class. It’s important to use them in situations it’s crystal clear what these operations are doing. Otherwise, it can lead to mistakes instead of helping.

And remember you can learn this and many other things about Kotlin in the book I’m writing: Kotlin for Android Developers, where you will learn Kotlin by creating an Android App from the ground up.

Author: Antonio Leiva

I’m in love with Kotlin. I’ve been learning about it for a couple of years, applying it to Android and digesting all this knowledge so that you can learn it with no effort.