In the Kotlin docs about Generic Constraints, it specifically mentions that it’s possible to create an upper bound for a generic, but it says nothing about lower bounds. Suppose I’m trying to implement an Optional:
sealed class Optional<T> {
abstract fun or(other: Optional<T>): Optional<T>
data class Present<T>(val value: T) : Optional<T>() {
override fun or(other: Optional<T>) = this
}
object Absent : Optional<Nothing>() {
override fun or(other: Optional<Nothing>) = other
}
}
fun main() {
println(Optional.Present(5).or(Optional.Present(3)))
}
Of course, this has several problems:
Absent can’t be used effectively, because T cannot become out T due to the input Optional<T>.
It’s impossible to use Optional.Present(5).or(Optional.Present(3.0)) to get an Optional<Number>
This requires the ability for the method or to take a type parameter T2 that has a lower-bound of T, so that if T is Int, T2 may be Int, Number, or Any.
It’s possible to achieve this with an extension method:
sealed class Optional<out T> {
data class Present<T>(val value: T) : Optional<T>()
object Absent : Optional<Nothing>()
}
fun <T> Optional<T>.or(other: Optional<T>) = when (this) {
is Optional.Present -> this
Optional.Absent -> other
}
fun main() {
println(Optional.Present(5).or(Optional.Present(3.0)))
println(Optional.Present(5).or(Optional.Absent))
println(Optional.Absent.or(Optional.Present(2)))
}
However, this would be impractical in any scenario that relies on extensibility, allowing users of the library to write their own implementations. (I only use Optional here as an easy example.)
How can I achieve the same generic allowances as the extension method, while maintaining the extensibility of an abstract function? To my knowledge, it is impossible in Java, but I’m hoping that Kotlin’s more advanced type system has a way.
I’m confused about this too, so unfortunately can’t give you a clean answer. But a couple points…
I think the docs do talk about lower bounds. Isn’t that <in Foo>, which corresponds to Java’s <? super Foo>? But, I don’t see how that helps you here. It seems like what you want is something that I don’t know how to do in Kotlin…
// not real code
abstract fun or(other: Option<U>): Option<common base of T and U>
Your example code:
Optional.Present(5).or(Optional.Present(3.0))
Is returning an Optional<Any> for me, not Optional<Number>. So I’m not sure that’s really working how you want. I see this by assigning it to something, putting the cursor over the val’s name, and going to the menus > View > Type Info.
Unfortunately, no, this method actually creates a function-local generic T that shadows the class-local generic T. It still works for the extension method because the function and class both must share the same generic T.
I guess the problem is that you try to restrict S not T. While logically T is a subtype of S and S is a supertype of T is equivalent this does not work for where clauses. Apparently you can only restrict the type in front of the colon and here we try to restrict the type after it.
I don’t think this is possible in kotlin. This might be an interesting feature. I personaly can’t remember ever needing this, but then I might have subconciously worked around it because it is not possible (and I don’t think this is possible in java or C# either, which are the other 2 languages with similar generics that I use(d) a lot).
Here [B >: A] indicates a type parameter B with a lower type bound (B must be a Supertype of A or the same type). The opposite and more common thing would be [B <: A] in Scala.