Consider the following humble example:
class Container<out T>(val element: T): Iterable<T> {
override fun iterator(): Iterator<T> = listOf(element).iterator()
override fun spliterator(): Spliterator<T> = listOf(element).spliterator()
fun x(): Spliterator<T> = listOf(element).spliterator()
}
fun <T> Container<T>.y(): Spliterator<T> = listOf(element).spliterator()
In this example code, I’m defining a hopefully straightforward Container class, which has a parameter of type T
, and these things should be covariant with one another, so it’s declared as such in the type parameter <out T>
.
We want the Container to be Iterable, so we declare it as such. Iterable becomes kotlin.collections.Iterable
, which is just fine because it’s also declared <out T>
. But with Kotlin/JVM, this is also java.lang.Iterable
, which has a spliterator()
method of its own, so you need to override it, right?
Line 3 of the example code above has an error according to Kotlin 1.3.61: Type parameter T is declared as 'out' but occurs in 'invariant' position in type Spliterator<T>
. So fine, I tried doing it without any overrides with fun x()
on line 4. Same error. The bottom line, with the y()
function seems to work, but you cannot use the specific name spliterator()
instead of y()
, since that would shadow the method name from java.lang.Iterable
.
What if I change Container<out T>
to Container<T>
? This lets the code compile, but now the class doesn’t have the desired substitutability, which was the whole point of the exercise.
I also tried override fun spliterator(): Spliterator<@JvmSuppressWildcards T>
but this didn’t change anything.
Wrapping this up: what’s the right way to declare a container class, as above, that has declared covariance, is Iterable
, and plays nice with Java’s Spliterator
?
I think that what I really want is some way to say “never mind that declaration above, this time it’s just T”. The only available workaround, so far as I can tell, is to get rid of the <out T>
and instead make sure that each and every input parameter that accepts a Container declares covariance at the use site, just like Java programmers are forced to do.
Am I missing something? Is there some sneaky way to narrow the type parameter from <out T>
to <T>
to be compatible with java.lang.Iterable.spliterator()
?