Search code examples
kotlinmethod-referencerx-kotlin

RxKotlin collectInto() MutableList using method references


The following code is my attempt to convert an RxJava example into Kotlin. It's supposed to collect a bunch of Int's into a MutableList, but I get a host of errors.

val all: Single<MutableList<Int>> = Observable
        .range(10, 20)
        .collectInto(::MutableList, MutableList::add)

The errors:

    Error:(113, 36) Kotlin: Type inference failed: Not enough information to infer parameter T in inline fun <T> MutableList(size: Int, init: (index: Int) -> T): MutableList<T>
Please specify it explicitly.

Error:(113, 49) Kotlin: One type argument expected for interface MutableList<E> : List<E>, MutableCollection<E> defined in kotlin.collections

    Error:(113, 67) Kotlin: None of the following functions can be called with the arguments supplied: 
public abstract fun add(element: Int): Boolean defined in kotlin.collections.MutableList
public abstract fun add(index: Int, element: Int): Unit defined in kotlin.collections.MutableList

If I change ImmutableList::add to ImmutableList<Int>::add, I get rid of the type argument expected error, which is replaced with:

Error:(113, 22) Kotlin: Type inference failed: fun <U : Any!> collectInto(initialValue: U!, collector: ((U!, Int!) -> Unit)!): Single<U!>!
        cannot be applied to
        (<unknown>,<unknown>)

This is a straight copy of the following in Java:

Observable<List<Integer>> all = Observable
    .range(10, 20)
    .collect(ArrayList::new, List::add);

I understand that the first error is telling me it's either inferring the incorrect type and I need to be more explicit (where?), but I thought that ::MutableList would be the equivalent of () -> MutableList<Int>. The third error is telling me that it can't call any of the add() methods with the arguments, but again, I thought that MutableList::add is equivalent to { list, value -> list.add(value) }. The fourth error tells me it can't figure out the types being applied to the collector.

If I use a lambda expression instead, there are no errors:

val all: Single<MutableList<Int>> = Observable
        .range(10, 20)
        .collectInto(mutableListOf(), { list, value -> list.add(value) })

all.subscribe { x -> println(x) }

I'd appreciate some comments on what I'm doing wrong with the method references, as there's clearly something I've misunderstood (looking through the Kotlin Language Reference, I'm wondering if it's even a language feature at this time?). Much appreciated.


Solution

  • In your first example, you try to apply the method signature of collect to the one from collectInto.

    This can never work, since collect expects a Func0<R> and a Action2<R, ? super T> and collectInto expects a real object and a BiConsumer<U, T>.
    A constructor reference can't work for collectInto - you need a real object (e.g. your mutableListOf() call)

    The second problem is that Kotlin is expecting a BiConsumer object and not a function. I'm not quite sure why. Apparently Kotlin can't handle multiple generics for lambdas and function reference from SAM-Interfaces.

    You therefore need to pass an instance of BiConsumer and not just a function.
    That's also why I asked in the comment whether you're sure about the error message:

    range(10, 20).collectInto(mutableListOf(), { l, i ->  l.add(i) }) 
    

    will give me an error, while

    range(10, 20).collectInto(mutableListOf(), BiConsumer { l, i ->  l.add(i) })
    

    won't.