Search code examples
kotlincollectionsnullablenon-nullable

Inferred type is Collection of nullables but expected Collection of non-nullables


I intended to process some data (it's a list of parsed from json objects Nuu, in the example below I just stubbed the list). To process that collection I pass a consumer println into the method getAllNuu that retrieves and parses that "json".

Although, the data I'm receiving from json contains duplicates (based on two [Nuu.lo, Nuu.la] of three fields, see overridden Nuu.equals - generated), so I'd like to merge them together by using extension function Nuu.merge

I don't see any error until I try to compile the code below, then I get: Error:(7, 5) Kotlin: Type mismatch: inferred type is Collection<Nuu?> but Collection<Nuu> was expected

Why would kotlin infers a collection with nullable objects? Where does it happens?

I would also appreciate any suggestions to make the code better or more kotlin-like.

My environment: kotlinc-jvm 1.3.70 (JRE 12.0.1+12)

Code:

    fun main(args: Array<String>) {
        getAllNuu { data -> println(mergeNuus(data)) }
    }

    fun mergeNuus(data: Collection<Nuu>): Collection<Nuu> =
        // grouping two same (based on their equals) elements
        data.groupingBy { it }
     // ^ Error:(7, 5) Kotlin: Type mismatch: inferred type is Collection<Nuu?> but Collection<Nuu> was expected
            // merging the equal elemnts into one
            .aggregate { _, accumulator: Nuu?, element: Nuu, first ->
                if (first)
                    element
                else
                    accumulator!!.merge(element)
            }.values

    // supposedly this is a http request which processes
    // the retrieved data by passed in consumer 'printout'
    fun getAllNuu(printout: (data: Collection<Nuu>) -> Unit) {
        val nuuList = listOf(
            Nuu(3, "t", 1),
            Nuu(5, "6", 2),     // say this is a http request
            Nuu(7, "a", 3),     // just been stubbed with the list
            Nuu(3, "a", 4),
            Nuu(5, "5", 5),
            Nuu(2, "2", 6),
            Nuu(3, "t", 7),
            Nuu(1, "1", 8),
            Nuu(5, "5", 9),
            Nuu(2, "2", 10)
        )
        // processing the data with consumer passed
        // from the main method
        printout.invoke(nuuList)
    }

    data class Nuu(
        val lo: Int,
        val la: String,
        val su: Int
    ) {
        override fun equals(other: Any?): Boolean {
            if (this === other) return true
            if (javaClass != other?.javaClass) return false
            other as Nuu
            if (lo != other.lo) return false
            if (la != other.la) return false
            return true
        }

        override fun hashCode(): Int {
            var result = lo
            result = 31 * result + la.hashCode()
            return result
        }
    }
    // similarity based on equals, and sumup the field 'su'
    fun Nuu.merge(other: Nuu) = Nuu(lo, la, su + other.su)

Thanks


Solution

  • In addition to the workaround I gave in comments (.aggregate<Nuu, Nuu, Nuu>) this also works without specifying any additional types:

        data.groupingBy { it }
            // merging the equal elemnts into one
            .aggregate { _, accumulator: Nuu?, element: Nuu, first ->
                val res = 
                    if (first)
                        element
                    else
                        accumulator!!.merge(element)
                res
            }.values
    

    I really can't see any reason for your code not compiling when this does, and it seems like a compiler bug to me. Of course, I don't see why a bug would happen here either, you aren't doing anything tricky or unusual.