Why should I implement a function type as an interface in Kotlin

849 Views Asked by At

I came across something and wondered all the time why you should do this.

You implement an interface in Kotlin through a simple function type:

"It is possible for a class to implement a function type as if it were an interface. It must then supply an operator function called invoke with the given signature, and instances of that class may then be assigned to a variable of that function type:"

class Divider : (Int, Int) -> Double {
    override fun invoke(numerator: Int, denominator: Int): Double = ...
}

But why should I do this? Why should I add an interface in that way? I think its only possible to add one function and not more. Or is it an advantage that I can implement a function with a function body and not only the function head like in normal interfaces? I think it is possible in Java to add default methods to interfaces with a function body. So maybe it is something like that?

2

There are 2 best solutions below

0
Mikhail Burshteyn On

It is probably not very useful to write a class that only implements a function type interface; however, it might be useful to write a class that can among other things be used in place of a function.

An example from the standard library is the KProperty1 interface. You can write code like this:

data class C(val id: Int, val name: String)
val objs = listOf(C(1, "name1"), C(2, "name2"), C(3, "name3"))
val ids = objs.map(C::id)

Here, C::id is a property reference of type KProperty1<C, Int>, and it can be used as an argument to List.map in place of a lambda because KProperty1<C, Int> extends (C) -> Int. However, KProperty1 has a lot of other uses besides being passed as a function.

2
Rene On

Function as a class can have state. For example you could store the last invocations and use the history as a cache:

class Divider : (Int, Int) -> Double {
    val history = mutableMapOf<Pair<Int, Int>, Double>()

    override fun invoke(numerator: Int, denominator: Int): Double {
        return history.computeIfAbsent(Pair(numerator, denominator)) {
            numerator.toDouble() / denominator.toDouble()
        }
    }
}

fun main() {
    val divider = Divider()
    println(divider(1,2))
    println(divider(2,3))
    println(divider.history)
}