genericskotlinerasure

Setter for field is removed by type projection


I have the following SSCCE:

class Foo(val bars: Map<Int, Bar<*>>) {

    fun <Baz> qux(baz: Baz) {
        val bar2 = bars[2]!!
        bar2.bazes += baz
    }

    interface Bar<Baz> {
        var bazes: MutableList<Baz>
    }
}

This seems fine to me, but the compiler complains that:

Error:(5, 9) Kotlin: Setter for 'bazes' is removed by type projection

I have no idea what this even means, much less how to correct it. What's going on here and how do I get around it?


Solution

  • There's a couple little issues. Temporarily using bars[2]!! as Bar<Baz>,

    w: (4, 20): Unchecked cast: Foo.Bar<Any?> to Foo.Bar<Baz>
    e: (5, 20): Assignment operators ambiguity: 
    public operator fun <T> Collection<Baz>.plus(element: Baz): List<Baz> defined in kotlin.collections
    @InlineOnly public operator inline fun <T> MutableCollection<in Baz>.plusAssign(element: Baz): Unit defined in kotlin.collections
    

    Kotlin doesn't know whether to handle it as bar2.bazes = bar2.bazes.plus(baz) or bar2.bazes.plusAssign(baz). If you change it to var2.bazes.add(baz) or val bazes the ambiguity goes away.

    Fixing that and removing the unsafe cast brings up

    e: (5, 20): Out-projected type 'MutableList<out Any?>' prohibits the use of 'public abstract fun add(element: E): Boolean defined in kotlin.collections.MutableList'
    

    the question of what can safely be done with the type projection. It gets treated like out Any?, so you can read from the list, but in Nothing, which means you can't add anything to the list.

    It's unclear from this example why you're using *. If it doesn't cause any other issues, perhaps you could lift out the <Baz> parameter, e.g.

    class Foo<Baz>(val bars: Map<Int, Bar<Baz>>)