I used to program in Java a lot, have been learning Swift for work recently, and now finally got around to tinkering with Kotlin.
One lack I immediately feel coming from Swift is that interfaces can not require certain constructors to exist (or, less relevant but also interesting, static functions/members). For instance, I could do this:
protocol JsonRepresentable {
init(fromJson: String)
func asJson(): String
}
Very clean, very neat. Note that nice things are possible now: if I have a T: JsonRepresentable
somewhere, I can call T(myJson)
and get an object of type T
!
Kotlin does not have this feature. I don’t have a good intuition yet which kinds of things are due to language design and which to limitations of Java/JVM. If it’s a language design thing here, the solution is “easy”, so I’ll assume it’s some kind of limitation.
So here is a Kotlin-ish idea for a workaround: Let interfaces specify companion objects and their members.
We can already write down this (yes, I know that Self
is obsolete here):
interface JsonRepresentable<Self: JsonRepresentable<Self>> {
fun toJson(): String
interface Companion<Outer: JsonRepresentable<Outer>> {
operator fun invoke(json: String): Outer
}
}
But there does not seem to be a way to require Outer == Self
(let alone the implementing type), and that every JsonRepresentable
actually have a companion implementing the (suggestively) named interface; all we have here is two independent interfaces JsonRepresentable
and JsonRepresentable.Companion
.
I would like to propose that something like this be valid code (actually, I’d like there to be a proper meta type Self
as well, but that’s a separate issue):
interface JsonRepresentable<Self: JsonRepresentable<Self>> {
fun toJson(): String
companion {
operator fun invoke(json: String): Self
}
}
The intended semantics for the contract would be:
- Every implementation of
JsonRepresentable
must have a companion object. - It must implement (operator) method
invoke
.
That much can (or so I think) be implemented in the Kotlin compiler, using conventions for the first part. The main thing I’m not sure about is whether it’s possible to make type parameters available to inner classes/objects. I don’t see why not, but well.