When dealing with types that can be added, removed, compared, or joined, we often need to write a lot of tedious and repetitive code. But in Kotlin, we can write more expressive and concise code for these types with operator overloading.

In addition to Android, I love singing in glee clubs, so let’s use the glee club example to illustrate the benefits of operator overloading. Suppose we have a choir of singers, and we want to add a singer to the choir with the following code:

<! -- Copyright2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->

class Choir {
    private val singers = mutableListOf<Singer>()

    fun addSinger(singer: Singer) {
        singers.add(singer)
    }

    ...
}
Copy the code

We will add a new singer as shown in the following example:

choir.addSinger(singer)
Copy the code

However, it is more appropriate and natural to use += instead of this operation.

choir += singer
Copy the code

Read on to learn:

  • How to do this with operator overloading in Kotlin;
  • What kind of operators can be implemented and in which Android scenarios it is more advantageous to use operators;
  • Best practices to note when implementing operator overloading;
  • How the Kotlin compiler implements operator overloading.

The basis for operator overloading

Operator overloading allows you to implement a series of predefined operators of any type. Operators can be overridden by member functions or by extension functions that use the corresponding member function. For example, the + operator can be overloaded using the plus() function, and the += operator can be overloaded using the plusAssign() function. Note that operators do not affect each other: if you overload +, it does not affect ++.

To override an operator, you add the operator keyword before fun, and then specify which operator you want to override. If you do not add the operator keyword, the compiler will treat it as a normal Kotlin function and will not even compile it!

Here are the operators that can be overloaded in Kotlin:

△ See the documentation for a complete list of operators that can be overloaded and their corresponding functions

How to do

Ok, so here we go, how do we implement operator overloading in Kotlin?

Let’s use the choir class from the original example, where we need to override the += operator to add a singer.

<! -- Copyright2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->

class Choir {
    private val singers = mutableListOf<Singer>()

    operator fun plusAssign(singer: Singer) {        
        singers.add(singer)
    }
}
Copy the code

You can use operators like this:

<! -- Copyright2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->

data class Singer(val name: String)

fun main(a) {
    val choir = Choir()
    val singerMeghan = Singer("Meghan")
    choir += singerMeghan
}
Copy the code

Overloaded operators make code more concise and readable.

What other operators would you like to overload?

Often you need more than one operator, but overloading all operators of a custom type may not make sense. Excessive use of operator overloading can make code less readable. So take the time to think about which operators you can overload to make your code more readable.

We overload the += operator to add someone to the choir, but we might also want to see if that person is already a member of the choir. To do this, we need to override the contains function so that we can use the IN operator.

<! -- Copyright2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->

operator fun contains(s: Singer) : Boolean {
       return singers.contains(s)
}
Copy the code
<! Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 --> Data class Singer(val name: Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 --> Data class Singer(val name: String) fun main() { val choir = Choir() val singerMeghan = Singer("Meghan") choir += singerMeghan if(singerMeghan in choir){ println("Meghan is a part of the choir!" )}}Copy the code

Operator overloading in the extension

Operator overloading can also be used by extending functions. In this example, we override the += operator of the ViewGroup:

operator fun ViewGroup.plusAssign(other: View) = addView(other)
Copy the code

Adding a View to a viewGroup is now so easy!

viewGroup += view
Copy the code

Best practices from other languages

Operator overloading is also used in many other programming languages such as C++, Python, Swift, and PHP. While we don’t have definitive best practices in Kotlin yet, we can learn a few things from these languages:

  • Simplicity doesn’t always mean more readable code. Think about whether your code would really be more readable if you included operator overloading;
  • If the result of an overload doesn’t make sense in the context of the language, or if there is any ambiguity, you should consider using functions instead. For example, if you add two books, it’s not immediately clear what the end result will be. Will it be a new book? How would they be combined? If in doubt, you should use functions instead;
  • If an operator is overloaded, it should be considered that other operators are overloaded as well. For example, if you overload -, -=, consider being overloaded too. In our choir example, since we can add a singer with +=, we should also be able to remove a singer with -=.

How does this work?

Operator overloading is achieved by overwriting the operator’s standard function calls, for example, adding code for choir members:

<! -- Copyright2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->

val choir = Choir()
val singerMeghan = Singer("Meghan")
choir += singerMeghan
Copy the code

If we look at the decompiled Java code, we can see how it works:

<! -- Copyright2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->

Choir choir = new Choir();
Singer singerMeghan = new Singer("Meghan");
choir.plusAssign(singerMeghan);
Copy the code

The compiler simply replaces += with normal member function calls.

conclusion

Operator overloading must be used with caution, but if you use it properly, it is a powerful tool that can make your code more expressive and concise.

  • Make sure you use the operator keyword, otherwise Kotlin will treat the function as a normal function and the code will fail to compile;
  • Check whether operator overloading makes code more readable;
  • Think carefully about which operator overloading makes more sense for a type.