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.
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.