4

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.

junglie85
  • 1,243
  • 10
  • 30

1 Answers1

4

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.

Lovis
  • 9,513
  • 5
  • 31
  • 47
  • Thanks for the explanation. With a bit of reading, I came to a similar conclusion, although perhaps in not such technical terms! I have no errors; what version of Kotlin are you using? I have 1.1.2-5. – junglie85 Jun 16 '17 at 11:14
  • 1
    [Here's](https://github.com/AshleyByeUK/rxkotlin/blob/6f9bb30c7b1da970c6edb4b11527c29208609056/src/main/kotlin/uk/ashleybye/rxkotlin/operatorstransformations/AdvancedOperators.kt#L107-L130) my full implementation of this example. – junglie85 Jun 16 '17 at 11:23
  • @amb85 I'm also using 1.1.2-5 ¯\_(ツ)_/¯ – Lovis Jun 16 '17 at 11:43
  • who knows?! ¯\\_(ツ)_/¯ – junglie85 Jun 16 '17 at 14:09