Friday, 17 May 2024

Kotlin Interface 'Invokation'

In general I like Kotlin syntax a real lot (mainly the "almost everything is an expression" thing) but there are things that I find surprising/confusing (time ago we saw one of them). Recently, when reading a bit about Ktorm, the object-relation mapper, I came across some rather surprising syntax. The article itself mentions it and explains what's going on (otherwise I don't think I had manages to understand how that was working), but I think it'll be useful to me to put it here.


interface Department : Entity {
    companion object : Entity.Factory()
    val id: Int
    var name: String
    var location: String
}

val department = Department()


So the above code seems as if we were somehow creating an instance of an interface (Department), which is obviously not possible. I can think of 2 other situations where we write code that looks similar, but with a different explanation.

One case is the special syntax for functional interfaces/SAM conversions


val runnable = Runnable { println("This runs in a runnable") }

And the other case are object expressions with the object (of the anonymous class) implementing an interface. But the object: syntax, and the "class body" makes clear that we are not "instantiating that interface".


interface Writable {
    fun write(): String
}

//object expressions implementing interface, there's not () after the interface name
val o2 = object: Writable {
    var name = "Francois"
    override fun write() = name
}


The case we're dealing with in this post is a different thing. The original article explains it:

The Entity.Factory class overloads the invoke operator, so we can use brackets to call the companion object as it’s a function. The code creating a department object:

val department = Department()

That’s the charm of Kotlin, Department is an interface, but we can still create its instances, just like calling a constructor function.

What that means is that in the end that code that seemed as if we were invoking a sort of "interface constructor" indeed was calling the invoke method of the companion object of that interface, that is:


val department = Department()

// is right the same as:

val department = Department.Companion.invoke()

No comments:

Post a Comment