2

My goal is to have the interface methods accept the class type of the implementation. Here's the code I have written so far:

internal interface Diff<Diff> {   //in Java I was using <? extends Diff>
    fun lessThan(other: Diff): Boolean
}

private class ADiff(private val value: Int) : Diff<ADiff> {

    override fun lessThan(other: ADiff): Boolean {
        return value < other.value
    }

}

//B can now accept even int types which is not desired
private class BDiff(private val value: Int) : Diff<Int> {

    override fun lessThan(other: Int): Boolean {
        return value < other
    }

}
dev
  • 11,071
  • 22
  • 74
  • 122

1 Answers1

3

The reason why this "worked" in Java was because <T extends Diff> uses the raw type Diff. Don't do this!


The closest you can get is to use a recursive type bound:

interface Diff<T : Diff<T>> {
    fun lessThan(other: T): Boolean
}

The problem is, you can substitute any other subtype of Diff.

However, when using the Diff, use the generic type constraint T : Diff<T>:

fun <T : Diff<T>> diffUser(diff1: T, diff2: T) {
    println(diff1.lessThan(diff2))
}

and any Diff that doesn't implement Diff<SameType> will not be accepted.

Example:

class CDiff(private val value: Int) : Diff<DDiff> { // <-- Wrong type!
    override fun lessThan(other: DDiff) = value < other.value
}

class DDiff(val value: Int) : Diff<DDiff> {
    override fun lessThan(other: DDiff) = value < other.value
}

fun test() {
    diffUser(CDiff(3), CDiff(4)) // <-- Doesn't compile due to the generic constraint
    diffUser(DDiff(3), DDiff(4))
}

This same approach is used by the Comparable class.


Although this works, what you really want is a "self type" and this is not supported, although it was on the roadmap at some point. I believe JetBrains denied the request for this, though I cannot find the bug report.

This answer details a workaround in Java using the CRT pattern, though it is not necessarily type safe.

Salem
  • 13,516
  • 4
  • 51
  • 70