Variance and output parameters

I’m learning Kotlin, so I am very new to the language.

I just read about variance in generics.

The trade-off of “declaration-site variance” (compared to what Java does) is interesting (by marking generic parameters in or out). However, there is no way to mark function parameters as in or out, so it assumes that function parameters are necessarily input parameters (for simplicity I guess).

As a consequence, this is not possible:

interface Source<out T> {
    fun copyTo(to: Collection<T>)
}

But worse, if we don’t constraint T to be an output parameter so that it compiles:

interface Source<T> {
    fun copyTo(to: Collection<T>)
}

then the compiled code is incorrect (kotlinc a.kt && procyon Source):

public interface Source<T>
{
    void copyTo(@NotNull final Collection<? extends T> p0);
}

The expected parameter type is Collection<? super T>, not Collection<? extends T> (semantically, it’s an output parameter).

Is there any way to mark a parameter as “output” (to override the default behavior which always considers parameters as “input”) regarding the variance rules?

My last exemple is incorrect, since Collection is read-only and only produces the generic type (it may not consume it):

public interface Collection<out E> : Iterable<E>

With a MutableCollection:

public interface MutableCollection<E> : Collection<E>, MutableIterable<E>
interface Source<T> {
    fun copyTo(to: MutableCollection<T>)
}

the generated code is “correct” (the parameter has not Collection<? extends T>)

public interface Source<T>
{
    void copyTo(@NotNull final Collection<T> p0);
}

This avoids the unsoundness issue, but does not solve the initial problem: how to express a generic output parameter with correct variance rules?

[…] there is no way to mark function parameters as in or out […]

Yes there is:

interface Source<T> {
    fun copyTo(to: MutableCollection<in T>)
}

And adding to above, we can do even this:

interface Source<out T> {
    fun copyTo(to: MutableCollection<in T>)
}

Exactly what you need.

@rom1v , I’m not sure what kind of trade-off do you mean. Kotlin supports both declaration-site and use-site variance.

2 Likes

Oh, indeed. Great! Thank you.