Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 619
Ky. Avatar asked Sep 20 '25 00:09

Ky.


1 Answers

The issue: Your bars property is using "star projection", which, put simply, just means: "I don't know the generic type of Bar". This in turn leads to a problem: You'll only be able to get something from the MutableList, adding is prohibited (Thus the error message: No Setter for bazes!).

A possible solution:

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

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

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

What I changed here, is that I made Foo typed with in Baz, which means Foo is made contravariant in its type parameter, thus Bazs can only be consumed. The same type is then used for the input parameter bars. As a result, you're now able to add values into bars.bazes, because the associated MutableList is said to be a "Consumer" of Bars.

btw: Using Baz as a name for the generic type isn't recommended - You should rather use T which is more obvious to be a generic type.

I know, it's a complex topic and I really advise to consult the good documentation as a further step :)

The plusAssign issue

The usage of += (plusAssign operator) on the other hand is a bit weird: The compiler complains not to be able to choose the correct operator:

Assignment operators ambiguity:
public operator fun Collection.plus(element: Any?): List >defined in kotlin.collections

@InlineOnly public operator inline fun MutableCollection.plusAssign(element: Baz): Unit defined in kotlin.collections

I tried to explicitly cast bazes to MutableList which in fact solves the issue. The compiler then complains about an unecessary cast though. I don't know how to handle this properly to be honest except using add instead ;-)

like image 169
s1m0nw1 Avatar answered Sep 22 '25 19:09

s1m0nw1