Kotlin supports a technique called conventions, everyone should be familiar with. For example, if you define a special method plus in your class, you can use the + operator by convention: That’s called Kotlin Operator Overloading.

In this article, I want to show you which conventions you can use and I will also provide a few Kotlin code examples that demonstrate the concepts. Kotlin defines conventions that we can apply by implementing methods that comply with predefined names like plus. This is contrary to how Java does provide equivalent language features, which is by specific classes like Iterable to make objects usable in for loops for example.

In the following descriptions, I’m going to use a class representing a mathematical fraction. This class will not be complete and also won’t provide mathematically perfect implementations. Just in case, you feel like knowing a much better implementation.

data class Fraction(val numerator: Int, val denominator: Int) {
    val decimal by lazy { numerator.toDouble() / denominator }

    override fun toString() = "$numerator/$denominator"
}

We have a data class with two user-defined properties: numerator and denominator. If we print a Fraction to the console, it’s supposed to look like “2/3”, so the toString() is being overridden. Also, for comparing two Fraction instances, a lazy property is included for providing the decimal value of the fraction.

Arithmetic Operators

Overloading operators enables us to use + in other classes than Int or String, you can use Kotlin’s predefined naming conventions to provide this functionality in any class. Let’s add it to our Fraction and see how it’s done.

data class Fraction(val numerator: Int, val denominator: Int) {
    //...

    operator fun plus(add: Fraction) =
            if (this.denominator == add.denominator){
                Fraction(this.numerator + add.numerator, denominator)
            } else {
                val a = this * add.denominator //(1)
                val b = add * this.denominator
                Fraction(a.numerator + b.numerator, a.denominator)
            }

    operator fun times(num: Int) = Fraction(numerator * num, denominator * num)

}

As you can see here, the plus method is defined as an operator function by using the operator keyword, which is relevant because otherwise, the compiler wouldn’t treat plus as a special function complying with a convention. As a second binary operator, times is implemented and also used in the implementation of plus, as you can see in (1). The statement this * add.denominator is translated to this.times(add.denominator) by the compiler. Let’s see how the Fraction can be used outside of this class now.

var sum = Fraction(2, 3) + Fraction(3, 2)
println("Sum: ${sum.decimal}")

As expected, adding two fractions with + is now possible, as well as using * would be as we also provided a times function. If we wanted to be able to multiply a Fraction with another Fraction, we’d have to overload the times function to accept a Fraction parameter instead of an Int. Thus, it isn’t a problem to provide multiple operator functions with different parameter types.

Besides binary operators, we can also overload unary operators to do something like negating a fraction: -Fraction(2,3) or incrementing/decrementing by using --/++. Please have a look at the documentation to learn how the naming conventions look like for these cases.