I have ```sealed class Bounds<in T : Comparable...
# getting-started
r
I have
Copy code
sealed class Bounds<in T : Comparable<T>> {
    abstract operator fun contains(value: T): Boolean
}
And I want
Copy code
object Unbounded : Bounds<Any?> {
    override fun contains(value: Any?) = true
}
However, I cannot as
Any?
violates the type bound. Is there a way to specify the type to make this work?
s
Typically this is done with the
@UnsafeVariance
annotation — for example the built-in collections do this
r
Unsafe variance is for violating
in
and
out
. My goal is to specify that
Unbounded
works for any comparable.
s
You’re right, I misunderstood the question, sorry
👍 1
y
Apparently this works:
Copy code
sealed class Bounds<in T : Comparable<T>> {
    abstract operator fun contains(value: T): Boolean
}
object Unbounded : Bounds<Comparable<Comparable<*>>>() {
    override fun contains(value: Comparable<Comparable<*>>) = true
}
🤯 1
👌 1
Not sure why the nested `Comparable`s are needed, but hey it runs at least
s
Another solution is
Copy code
class Unbounded<in T : Comparable<T>> : Bounds<T>() {
    override fun contains(value: T) = true
}
if you can live with it not being a singleton
y
I would've expected that
Comparable<*>
would be a subtype of
Comparable<T> where T: Comparable<T>
but I guess not?
This also works:
Copy code
sealed class Bounds<in T : Comparable<T>> {
    abstract operator fun contains(value: T): Boolean
}
object Unbounded : Bounds<Comparable<Any?>>() {
    override fun contains(value: Comparable<Any?>) = true
}
Which I think (maybe?) makes more sense. Comparable's T is
in
, and so
Comparable<*>
is in reality
Comparable<Nothing>
, which I think then isn't a subtype of
Comparable<Comparable<Nothing>>
because it can only accept arguments of type
Nothing
. Hopefully that makes a bit more sense lol! Generic variance is truly
out
of this world...
😆 1
r
@Youssef Shoaib [MOD] Those get rid of the error, but they don't actually work:
Copy code
interface B : Comparable<B>
class A(val spec: Bounds<B> = Unbounded)  // Error is now here
@Sam Yeah, that's what I'm currently using, but I was hoping for a singleton solution to match another part of the API more closely.
y
I think the easiest solution is this:
Copy code
sealed class Bounds<in T : Comparable<T>> {
    abstract operator fun contains(value: T): Boolean
}

object Unbounded : Bounds<Comparable<Any?>>() {
    override fun contains(value: Comparable<Any?>) = true
}

fun <T: Comparable<T>> unbounded(): Bounds<T> = Unbounded as Bounds<T>

interface B : Comparable<B>

class A(val spec: Bounds<B> = unbounded())
r
Indeed, that works. Feels a bit hacky, but may be the best option.
m
late response, but this looks a lot like my range custom range implementation that looks like this for 'unbounded' ranges:
Copy code
sealed interface Range<T : Comparable<T>>{
    interface Open<T : Comparable<T>> : Range<T> {
        override fun contains(value: T) = true
    }
}

internal object OpenRange = Range.Open<Nothing>

fun <T : Comparable<T>> openRange() = Range.OPEN as Open<T>
its akin to how List<T> and emptyList() is used by kotlin self, you only have a cast problem, but you'll have to surpress that. Kotlin itself uses unsafevariance and stuff to surpress the cast warnings. The main takeaway is that unbounded should probably be Nothing, not Any?