Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

combineLatest/zip array overload to be typesafe #173

Open
dmstocking opened this issue Feb 26, 2018 · 4 comments
Open

combineLatest/zip array overload to be typesafe #173

dmstocking opened this issue Feb 26, 2018 · 4 comments

Comments

@dmstocking
Copy link

I wanted to use the version of combineLatest that takes an array/iterable. The only problem is that it gives you a Array<Any> which wasn't very useful. I thought it was kind of weird that RxJava doesn't have a version that gives you Array<T>. Was this because of the Java compiler couldn't figure out the two overloads? Anyway, would we want to provide a version in this project that correctly has the type T for the array? Maybe there is a good reason not to, but I found it really useful for what I was working on.

I made one myself by using both Kotlin and Java together to do what I think is the most efficient version while still being useful.

inline fun <reified T: Any,R> Observables.combineLatest(observables: Array<Observable<T>>,
                                                        crossinline combiner: (Array<T>) -> R)
        : Observable<R> =
        Observable.combineLatest(
                observables,
                { items -> combiner.invoke(ArrayUtils.cast(items, arrayOfNulls<T>(items.size))) },
                Observable.bufferSize())
    @NotNull
    public static <T> T[] cast(@NotNull Object[] items,
                               @NotNull T[] ts) {
        return Arrays.asList(items).toArray(ts);
    }

This uses Kotlin to actually create the array we are copying to. We can do this because the type is reified. The java then uses the Arrays.asList to create a simple list wrapper for the array and then copies it into the array we made. So we make one copy of the array and a list wrapper for the array.

@JakeWharton
Copy link

In practice this will be the exact same thing as the generic parameter will be widened to Any. For example, combineLatest(listOf(just(1), just("hey")), values -> /* it's Array<Any> here */). Reification doesn't offer anything here.

@akarnokd
Copy link
Member

It is explained in the JavaDoc:

Note on method signature: since Java doesn't allow creating a generic array with new T[], the implementation of this operator has to create an Object[] instead. Unfortunately, a Function<Integer[], R> passed to the method would trigger a ClassCastException.

As the apply method first tries to cast the Object[] into Integer[] which is not possible. I'd guess Kotlin has the same restrictions.

@dmstocking
Copy link
Author

Man you guys are fast lol.

@JakeWharton I will admit it doesn't help when T is widened. My specific use case was using it like this. (I changed the name of some types)

combineLatest(viewModelRequests) { requests ->
    val loading = request.any { it is Request.Loading }
    val items = request.mapNotNull { it.successDataOrNull() }
            .flatmap{ it } // the type of "it" is List<ListItemViewModel>
    return@combineLatest ViewModel(loading, items)
}

I have an android recycler view that is populated from many sources and I combine them all as a list. It was easier then having the overload that took five observables. I basically had a list that I would unpack for combineLatest then immediately repack it back into a list.

@akarnokd Kotlin does have the same restrictions for non inline methods. For inline methods, you can make the type reified this is basically like the compiler automatically adding a parameter Class<T> clazz to your method. This let me make the array, new T[], in Kotlin. This is how I sort of cheated around it in Kotlin. I then pass it to Java to do the array copy. I could see that the RxJava project didn't want to add a overload that used Class<T> clazz if it wasn't going to be used very often.

@balazsbanyai
Copy link

balazsbanyai commented Mar 18, 2022

This does the trick:

inline fun <reified T> Iterable<Single<T>>.zip(): Single<List<T>> {
    return Single.zip(this) {
        it.filterIsInstance<T>()
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants