I'm having a mental block trying to work with gene...
# getting-started
k
I'm having a mental block trying to work with generics. 🧵
I'm trying to write a function that returns two comparables of the same type, but it won't compile:
Copy code
fun <T: Comparable<T>> getComparables() : Pair<T, T>
    = when (Random.nextBoolean()) {
        true -> Pair(1, 2)
        false -> Pair("a", "b")
    }
Also won't compile:
Copy code
fun <T> getComparables() : Pair<Comparable<T>, Comparable<T>>
    = when (Random.nextBoolean()) {
        true -> Pair(1, 2)
        false -> Pair("a", "b")
    }
This compiles but doesn't guarantee that the types are the same:
Copy code
fun getComparables() : Pair<Comparable<*>, Comparable<*>>
    = when (Random.nextBoolean()) {
        true -> Pair(1, 2)
        false -> Pair("a", "b")
    }
m
The problem with the first 2 is that if T is not Any, then what is being returned may not match T. If the function was valid you could say
getComparable<Instant>()
and it would return either a pair of string or a pair of ints, which does not implement
Pair<Instant>
. The only valid return type for this is
Pair<Any>
or
Pair<Comparable<Any>>
e
actually only
Pair<Any, Any>
is possible
neither
Int
nor
String
is
Comparable<Any>
, because
Comparable<in T>
is contravariant
if you want to define your own
Copy code
data class ComparablePair<T : Comparable<T>>(val first: T, val second: T)
then you could define
Copy code
fun getComparables(): ComparablePair<*>
but that's not all that much more useful
m
I feel stupid that I forgot that Pair has two generics
Maybe when we have union types, you could say that it returns
Pair<String, String> | Pair<Int, Int>
e
an untagged union would not be any more useful than
Pair<*, *>
due to erasure, and you can do a tagged union now with
sealed
y
What you need is that the consumer of the function gets an existential type as a result, so quantifying your generic here is never going to work. The
ComparablePair
is the solution here, to be consumed like:
Copy code
getComparables().doSomething()
fun <T: Comparable<T>> ComparablePair<T>.doSomething() = first < second // for example
k
Thanks everyone, ComparablePair sounds like what I need.