How to implement a Kotlin interface that refers to the conforming type?

1.1k views Asked by At

In the interest of silly thought experiments whose primary purpose is to explore how part of a language works, I decided I wanted to explore a method of making Python programmers more comfortable in Kotlin. Simplistically, I can do this by adding:

class Foo {
    private val self:Foo get() = this
    ...
}

(Aside Question: Is there a more generalized way to refer to Foo as the return type there so that if I changed Foo to Bar, the variable type of self would still refer to the "implementing class of this method"?)

Having to put that line in each class so that we can feel selfishly pythonic is tedious though. So I turned to an Interface. What I initially wanted is something like Swift's Self type for Protocols. But I couldn't find anything like that in Kotlin. After reading https://kotlinlang.org/docs/reference/generics.html (which seems to be about Java as much as Kotlin), I concluded that perhaps "Declaration Site Variance" was the thing for me:

interface Selfish<out T> {
    val self:T get() = this as T
}

class Foo:Selfish<Foo> {
}

This is better. It's undesirable that I have to list the class name twice in the declaration, but I don't think there's a way around that. Is there?

Additionally, this works for final classes, but if I want to have a class hierarchy that conforms to Selfish at the root level, things fall apart:

class Foo:Selfish<Foo> { ... }
class Bar:Foo { ... }

Methods in Bar that use self are of the wrong type. And adding , Selfish<Bar> creates a conflict.

Is there a tool I haven't discovered yet to make the type refer to inherited type?

Is there another approach (other than Interfaces) to do something like this?

Did I make the wrong choice using "Declaration Site Variance"?

2

There are 2 answers

0
Andrey Danilov On BEST ANSWER

I think you should take a look at extensions.

So you can write

fun <T>Foo.getSelf(): T {
    return this as T
}

Then if you have

open class Foo

class Bar: Foo()

so

Bar().getSelf<Bar>()

will return object of Bar class

Or even easier, you can write

fun <T:Foo>T.getSelf(): T {
    return this as T
}

so you can call just

Bar().getSelf()

to get instance of any class extended from Foo

0
Travis Griggs On

Per your suggestion @DEADMC, I went with a global extension val as opposed to fun. It didn't answer the question of how you refer generally to the conforming type in an implementation without using a generic pattern, but it DID solve the bigger issue in a much simpler and scalable way:

val <Anything>Anything.self:Anything inline get() = this