RxJava : if/else operation in flatMap - java

I try to get and render some data like below
raw data class
#Data #AllArgsConstructor class Category {
String name;
List<String> items;
}
presentation class
#Data #AllArgsConstructor class ViewModel {
public static final int TYPE_HEADER = 0;
public static final int TYPE_ITEM = 1;
int type;
String category;
String itemName;
}
and below code is request and transform subscribed data to presentation object.
Observable.create(new Observable.OnSubscribe<Category>() {
#Override public void call(Subscriber<? super Category> subscriber) {
subscriber.onNext(new Category("1", Lists.newArrayList("", "a", "b")));
subscriber.onNext(new Category("2", Lists.newArrayList("")));// this data does not output
subscriber.onNext(new Category("3", Lists.newArrayList("c", "", "d")));
subscriber.onNext(new Category("4", Lists.newArrayList("e", "f", "")));
}
}).flatMap(new Func1<Category, Observable<ViewModel>>() {
#Override public Observable<ViewModel> call(Category category) {
// TODO make this block to one line
// 1. clean response data and transform to ViewModel
List<ViewModel> cleanedItems = Lists.newArrayList(
Observable.from(category.getItems()).filter(new Func1<String, Boolean>() {
#Override public Boolean call(String s) {
return s != null && !s.isEmpty();
}
}).map(new Func1<String, ViewModel>() {
#Override public ViewModel call(String item) {
return new ViewModel(ViewModel.TYPE_ITEM, null, item);
}
}).toBlocking().toIterable());
if (cleanedItems.isEmpty()) {
// 2. case : skip
return Observable.empty();
} else {
// 3. case : add header and cleaned data
return Observable.concat(
Observable.just(new ViewModel(ViewModel.TYPE_HEADER, category.getName(), null)),
Observable.from(cleanedItems));
}
}
}).subscribe(new Action1<ViewModel>() {
#Override public void call(ViewModel viewModel) {
// render data
System.out.println(viewModel.toString());
}
});
output
ViewModel(type=0, category=1, itemName=null)
ViewModel(type=1, category=null, itemName=a)
ViewModel(type=1, category=null, itemName=b)
ViewModel(type=0, category=3, itemName=null)
ViewModel(type=1, category=null, itemName=c)
ViewModel(type=1, category=null, itemName=d)
ViewModel(type=0, category=4, itemName=null)
ViewModel(type=1, category=null, itemName=e)
ViewModel(type=1, category=null, itemName=f)
I try to write 1, 2, 3 (in comment) statement to 1 line (or more readable way), but I had no idea.
defaultIfEmpty operator seems not to use in this case.
Do anyone have any idea ?

How about this:
public static <T, R> Func1<? super T, ? extends Observable<? extends R>> ternary(
Func1<T, Boolean> predicate,
Func1<? super T, ? extends Observable<? extends R>> ifTrue,
Func1<? super T, ? extends Observable<? extends R>> ifFalse) {
return (item) -> predicate.call(item)
? ifTrue.call(item)
: ifFalse.call(item);
}
and you use it like this:
.map(CharSequence::toString)
.distinctUntilChanged()
.flatMap(ternary(Strings::notNullOrEmpty,
(kw) -> userRelationshipApi.searchFollowing(kw, null),
(kw) -> Observable.just(selectedUserListAdapter.getUsers())))
.subscribe(...)

AFAIK you cannot fullfil it using one line.
You have to determine if stream is empty and if not emit the items again. You can use cache to avoid double computations.
Observable.create(new Observable.OnSubscribe<Category>() {
#Override
public void call(Subscriber<? super Category> subscriber) {
subscriber.onNext(new Category("1", Lists.newArrayList("", "a", "b")));
subscriber.onNext(new Category("2", Lists.newArrayList("")));// this data does not output
subscriber.onNext(new Category("3", Lists.newArrayList("c", "", "d")));
subscriber.onNext(new Category("4", Lists.newArrayList("e", "f", "")));
subscriber.onCompleted();
}
}).flatMap(new Func1<Category, Observable<ViewModel>>() {
#Override
public Observable<ViewModel> call(Category category) {
final Observable<ViewModel> cleanedItems = Observable.from(category.getItems()).filter(new Func1<String, Boolean>() {
#Override
public Boolean call(String s) {
return s != null && !s.isEmpty();
}
}).map(new Func1<String, ViewModel>() {
#Override
public ViewModel call(String item) {
return new ViewModel(ViewModel.TYPE_ITEM, null, item);
}
}).cache();
return cleanedItems.isEmpty().flatMap(new Func1<Boolean, Observable<ViewModel>>() {
#Override
public Observable<ViewModel> call(Boolean aBoolean) {
if (aBoolean) {
return Observable.empty();
} else {
return Observable.concat(
Observable.just(new ViewModel(ViewModel.TYPE_HEADER, category.getName(), null)),
cleanedItems);
}
}
});
}
}).subscribe(new Action1<ViewModel>() {
#Override
public void call(ViewModel viewModel) {
// render data
System.out.println(viewModel.toString());
}
});
But instead of using rxjava whenever possible, sometimes you can use other api to make code simpler. In you case, you can for example use stream api:
.flatMap(new Func1<Category, Observable<ViewModel>>() {
#Override
public Observable<ViewModel> call(Category category) {
List<ViewModel> items = category.getItems().stream().filter(s -> s != null && !s.isEmpty()).map(s -> new ViewModel(ViewModel.TYPE_ITEM, null, s)).collect(Collectors.toList());
if (items.isEmpty()) {
return Observable.empty();
} else {
items.add(0, new ViewModel(ViewModel.TYPE_HEADER, category.getName(), null));
return Observable.from(items);
}
}
})

You can use operator filter() with switchIfEmpty(...)

Related

RxJava method conversion to RxJava3

this is a method written in RxJava
public Observable<String> method() {
return model.getOffers()
.filter(new Func1<Offers, Boolean>() {
#Override
public Boolean call(Offers offers) {
if (offers == null)
return false;
return offers.hasSuperOffer();
}
})
.flatMap(new Func1<Offers, Observable<Long>>() {
#Override
public Observable<Long> call(Offers offers) {
Long offerEndTime = offers.getRemainingTime();
if (offerEndTime == null) {
return Observable.empty();
}
AtomicLong remainingTimeSec;
Long currentTimeSec = System.currentTimeMillis() / 1000;
if (remainingTimeSec.get() == -1 && (offerEndTime > currentTimeSec)) {
remainingTimeSec.set(offerEndTime - currentTimeSec);
} else {
return Observable.empty();
}
return Observable.interval(1, TimeUnit.SECONDS)
.onBackpressureDrop()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.take(remainingTimeSec.intValue())
.doOnUnsubscribe(new Action0() {
#Override
public void call() {
}
})
.doOnCompleted(new Action0() {
#Override
public void call() {
}
})
.map(new Func1<Long, Long>() {
#Override
public Long call(Long elapsedTimeSec) {
return remainingTimeSec.getAndDecrement();
}
});
}
})
.map(new Func1<Long, String>() {
#Override
public String call(Long remainingTime) {
return DateUtils.getRemainingTimeStr(remainingTime);
}
});
}
I am trying to convert it to RxJava3 but some parameters have changed:
Func1 has been changed to Function
Action0 has been changed to Action
After I'm making the changes the following error appears at filter:
filter (io.reactivex.rxjava3.functions#io.reactivex.rxjava3.annotations.NonNull Predicate <? MyClass> in Observable cannot be applied to (anonymous.io.reactivex.rxjava3.functions.Function <MyClass.model.Offers.java.lang.Boolean>)
Can anyone help me?
Thank you!

Calling different methods based on values of two Optionals

While working with Java 8 Optionals I face following scenario very frequently. I have two Optional objects and then I want to call different methods based on the values (ifPresent) of those Optionals.
Here is an example:
void example(Optional<String> o1, Optional<String> o2) throws Exception {
if (o1.isPresent() && o2.isPresent()) {
handler1(o1.get(), o2.get());
} else if (o1.isPresent()) {
handler2(o1.get());
} else if (o2.isPresent()) {
handler3(o2.get());
} else {
throw new Exception();
}
}
However, this chain of if-else statements doesn't seem like a proper way of working with Optional (after all, they were added so that you can avoid writing these if-else checks everywhere in your code).
What is the proper way of doing this with Optional objects?
You said that you use such structure frequently, so I propose to introduce a Helper class:
final class BiOptionalHelper<F, S> {
private final Optional<F> first;
private final Optional<S> second;
public BiOptionalHelper(Optional<F> first, Optional<S> second){
this.first = first;
this.second = second;
}
public BiOptionalHelper<F, S> ifFirstPresent(Consumer<? super F> ifPresent){
if (!second.isPresent()) {
first.ifPresent(ifPresent);
}
return this;
}
public BiOptionalHelper<F, S> ifSecondPresent(Consumer<? super S> ifPresent){
if (!first.isPresent()) {
second.ifPresent(ifPresent);
}
return this;
}
public BiOptionalHelper<F, S> ifBothPresent(BiConsumer<? super F, ? super S> ifPresent){
if(first.isPresent() && second.isPresent()){
ifPresent.accept(first.get(), second.get());
}
return this;
}
public <T extends Throwable> void orElseThrow(Supplier<? extends T> exProvider) throws T{
if(!first.isPresent() && !second.isPresent()){
throw exProvider.get();
}
}
}
Which then may be used in a way like this:
new BiOptionalHelper<>(o1, o2)
.ifBothPresent(this::handler1)
.ifFirstPresent(this::handler2)
.ifSecondPresent(this::handler3)
.orElseThrow(Exception::new);
Though, this just moves your problem into a separate class.
Note: above code may be refactored to not use Optional and isPresent() checks at all. And just use null for first and second and replace isPresent() with null-checks.
As it is generally a bad design to store Optional in fields or accept them as parameters in the first place. As JB Nizet already pointed out in a comment to the question.
Another way it to move that logic into common helper method:
public static <F, S, T extends Throwable> void handle(Optional<F> first, Optional<S> second,
BiConsumer<F, S> bothPresent, Consumer<F> firstPresent,
Consumer<S> secondPresent, Supplier<T> provider) throws T{
if(first.isPresent() && second.isPresent()){
bothPresent.accept(first.get(), second.get());
} else if(first.isPresent()){
firstPresent.accept(first.get());
} else if(second.isPresent()){
secondPresent.accept(second.get());
} else{
throw provider.get();
}
}
Which then could be called like this:
handle(o1, o2, this::handler1, this::handler2, this::handler3, Exception::new);
But it's still kind of messy to be honest.
Disclaimer: My answer is based on Lino's answer - the first part of this answer (BiOptional<T, U>) is a modified version of Lino's BiOptionalHelper, while the second part (BiOptionalMapper<T, U, R>) is my idea for extending this nice pattern.
I like Lino's answer a lot. However, I feel that instead of calling it BiOptionalHelper, it deserves to be simply called BiOptional, provided that:
it gets Optional<T> first() and Optional<T> second() methods
it gets is(First/Second)Present, is(First/Second)OnlyPresent and are(Both/None)Present methods
if(First/Second)Present methods are renamed to if(First/Second)OnlyPresent
it gets ifNonePresent(Runnable action) method
orElseThrow method is renamed to ifNonePresentThrow
Finally (and this is the entirely original part of my answer), I realized this pattern could support not only "handling" (in BiOptional), but also "mapping" (in BiOptionalMapper obtained through BiOptional.mapper()), like that:
BiOptional<String, Integer> biOptional = BiOptional.from(o1, o2);
// handler version
biOptional
.ifBothPresent(this::handleBoth)
.ifFirstOnlyPresent(this::handleFirst)
.ifSecondOnlyPresent(this::handleSecond)
.ifNonePresent(this::performAction);
// mapper version
String result = biOptional.<String>mapper()
.onBothPresent(this::mapBoth)
.onFirstOnlyPresent(this::mapFirst)
.onSecondOnlyPresent(this::mapSecond)
.onNonePresent("default")
.result();
Optional<String> optionalResult = biOptional.<String>mapper()
.onBothPresent(this::mapBoth)
.onNonePresentThrow(IllegalStateException::new)
.optionalResult();
Note that one can either:
call all on*Present mapping methods, and then call R result() (which will throw if result were to be absent), or
call only some of them, and then call Optional<R> optionalResult()
Note also that:
in order to avoid confusion between "handling" and "mapping", the naming convention is as follows:
BiOptional: if*Present
BiOptionalMapper: on*Present
if any of the on*Present methods is called twice, BiOptionalMapper will throw if result were to be overwritten (unlike BiOptional, which can handle multiple if*Present calls)
result cannot be set to null by the mappers provided to on*Present or by calling onNonePresent(R) (Optional<...> should be used as result type R instead)
Here's the source code of the two classes:
final class BiOptional<T, U> {
#Nullable
private final T first;
#Nullable
private final U second;
public BiOptional(T first, U second) {
this.first = first;
this.second = second;
}
public static <T, U> BiOptional<T, U> from(Optional<T> first, Optional<U> second) {
return new BiOptional<>(first.orElse(null), second.orElse(null));
}
public Optional<T> first() {
return Optional.ofNullable(first);
}
public Optional<U> second() {
return Optional.ofNullable(second);
}
public boolean isFirstPresent() {
return first != null;
}
public boolean isSecondPresent() {
return second != null;
}
public boolean isFirstOnlyPresent() {
return isFirstPresent() && !isSecondPresent();
}
public boolean isSecondOnlyPresent() {
return !isFirstPresent() && isSecondPresent();
}
public boolean areBothPresent() {
return isFirstPresent() && isSecondPresent();
}
public boolean areNonePresent() {
return !isFirstPresent() && !isSecondPresent();
}
public BiOptional<T, U> ifFirstOnlyPresent(Consumer<? super T> ifFirstOnlyPresent) {
if (isFirstOnlyPresent()) {
ifFirstOnlyPresent.accept(first);
}
return this;
}
public BiOptional<T, U> ifSecondOnlyPresent(Consumer<? super U> ifSecondOnlyPresent) {
if (isSecondOnlyPresent()) {
ifSecondOnlyPresent.accept(second);
}
return this;
}
public BiOptional<T, U> ifBothPresent(BiConsumer<? super T, ? super U> ifBothPresent) {
if (areBothPresent()) {
ifBothPresent.accept(first, second);
}
return this;
}
public BiOptional<T, U> ifNonePresent(Runnable ifNonePresent) {
if (areNonePresent()) {
ifNonePresent.run();
}
return this;
}
public <X extends Throwable> void ifNonePresentThrow(Supplier<? extends X> throwableProvider) throws X {
if (areNonePresent()) {
throw throwableProvider.get();
}
}
public <R> BiOptionalMapper<T, U, R> mapper() {
return new BiOptionalMapper<>(this);
}
}
and:
final class BiOptionalMapper<T, U, R> {
private final BiOptional<T, U> biOptional;
private R result = null;
BiOptionalMapper(BiOptional<T, U> biOptional) {
this.biOptional = biOptional;
}
public BiOptionalMapper<T, U, R> onFirstOnlyPresent(Function<? super T, ? extends R> firstMapper) {
if (biOptional.isFirstOnlyPresent()) {
setResult(firstMapper.apply(biOptional.first().get()));
}
return this;
}
public BiOptionalMapper<T, U, R> onSecondOnlyPresent(Function<? super U, ? extends R> secondMapper) {
if (biOptional.isSecondOnlyPresent()) {
setResult(secondMapper.apply(biOptional.second().get()));
}
return this;
}
public BiOptionalMapper<T, U, R> onBothPresent(BiFunction<? super T, ? super U, ? extends R> bothMapper) {
if (biOptional.areBothPresent()) {
setResult(bothMapper.apply(biOptional.first().get(), biOptional.second().get()));
}
return this;
}
public BiOptionalMapper<T, U, R> onNonePresent(Supplier<? extends R> supplier) {
if (biOptional.areNonePresent()) {
setResult(supplier.get());
}
return this;
}
public BiOptionalMapper<T, U, R> onNonePresent(R other) {
if (biOptional.areNonePresent()) {
setResult(other);
}
return this;
}
public <X extends Throwable> BiOptionalMapper<T, U, R> onNonePresentThrow(Supplier<? extends X> throwableProvider) throws X {
biOptional.ifNonePresentThrow(throwableProvider);
return this;
}
public R result() {
if (result == null) {
throw new IllegalStateException("Result absent");
}
return result;
}
public Optional<R> optionalResult() {
return Optional.ofNullable(result);
}
private void setResult(R result) {
if (result == null) {
throw new IllegalArgumentException("Null obtained from a mapper");
}
if (this.result != null) {
throw new IllegalStateException("Result already present: " + this.result);
}
this.result = result;
}
}
It doesn’t really answer your question, but since Java 9 I would prefer something along these lines:
o1.ifPresentOrElse(s1 -> {
o2.ifPresentOrElse(s2 -> {
handler1(s1, s2);
}, () -> {
handler2(s1);
});
}, () -> {
o2.ifPresentOrElse(s2 -> {
handler3(s2);
}, () -> {
throw new IllegalArgumentException("Neither was present");
});
});
There’s a rule of thumb about Optional saying not to use isPresent and get. I do use them very occasionally; most often they are better avoided.
To avoid if statements or here if (Optional.isPresent()) you should have a common way to handle the Optional values but it is not the case as according their content you may invoke a function with the functional interface Consumer<String> or BiConsumer<String, String>.
As hint, you may factor the second part but it is not more readable or a better way :
if (o1.isPresent() && o2.isPresent()) {
handler1(o1.get(), o2.get());
} else {
Map<Optional<String>, Consumer<String>> map = new HashMap<>();
map.put(o1, this::handler2);
map.put(o2, this::handler3);
Optional<String> opt = Stream.of(o1, o2)
.filter(Optional::isPresent)
.findFirst()
.orElseThrow(Exception::new);
map.get(opt)
.accept(opt.get());
}
If you have much more Optionals to handle in this way such as this would probably make more sense but still it is a lot of code to write.
A more readable alternative could be to introduce a Rule class that stores the information required to trigger that if required :
public Rule(BiPredicate<Optional<String>, Optional<String>> ruleFunction, Runnable runnableIfApplied) {
this.ruleFunction = ruleFunction;
this.runnable = runnableIfApplied;
}
The BiPredicate<Optional<String>, Optional<String>> represents the matching function and the Runnable is the method to execute if the matching occurs.
You could move the rule execution logic in a Rule static method.
The idea is to make as clear as possible the rule specifications from the client side such as :
void example(Optional<String> o1, Optional<String> o2, Optional<String> o3) throws Exception {
Rule.executeFirstMatchOrFail(o1, o2,
new Rule((opt1, opt2) -> opt1.isPresent() && opt2.isPresent(), () -> handler1(o1.get(), o2.get())),
new Rule((opt1, opt2) -> opt1.isPresent(), () -> handler2(o1.get())),
new Rule((opt1, opt2) -> opt2.isPresent(), () -> handler3(o2.get())));
}
Rule could look like :
public class Rule {
static void executeFirstMatchOrFail(Optional<String> o1, Optional<String> o2, Rule... rules) throws Exception {
for (Rule rule : rules) {
if (rule.apply(o1, o2)) {
return;
}
}
throw new Exception();
}
private Runnable runnable;
private BiPredicate<Optional<String>, Optional<String>> ruleFunction;
public Rule(BiPredicate<Optional<String>, Optional<String>> ruleFunction, Runnable runnableIfApplied) {
this.ruleFunction = ruleFunction;
this.runnable = runnableIfApplied;
}
public boolean apply(Optional<String> o1, Optional<String> o2) {
if (ruleFunction.test(o1,o2)) {
runnable.run();
return true;
}
return false;
}
}

Get error and resubscribe

Is it possible to resubscribe an Observable and get the error?
The Observable<T> retry() method resubscribes the observable but it consumes the error.
final PublishSubject<Integer> observable = PublishSubject.create();
observable
.flatMap(new Func1<Integer, Observable<Integer>>() {
#Override
public Observable<Integer> call(final Integer integer) {
if (integer % 2 == 0) {
return Observable.just(integer);
} else {
return Observable.error(new Exception("int: " + integer));
}
}
})
.retry()
.subscribe(new Action1<Integer>() {
#Override
public void call(final Integer integer) {
Timber.i("integer: %d", integer);
}
},
new Action1<Throwable>() {
#Override
public void call(final Throwable throwable) {
Timber.e(throwable, "throwable");
}
},
new Action0() {
#Override
public void call() {
Timber.w("onCompleted");
}
});
Observable
.range(0, 10)
.delay(2, TimeUnit.MILLISECONDS)
.subscribe(new Action1<Integer>() {
#Override
public void call(final Integer integer) {
observable.onNext(integer);
}
},
new Action1<Throwable>() {
#Override
public void call(final Throwable throwable) {
observable.onError(throwable);
}
},
new Action0() {
#Override
public void call() {
observable.onCompleted();
}
});
The onError part of observable is never called because .retry() consumes the error.
What you're looking for is retryWhen(). This allows you to pass a Func1 which provides you with the Throwable, that means you can place your onError logic there instead.
This is a good article.

RxJava, get first item if exists

I'm trying to get the first item from a list using RxJava. However, I don't want it to throw an error if the item doesn't exist. Instead I want to be able to handle that myself by providing a default item.
The code I created below works correctly in retrieving the first item in a list. Though I can't figure out how to incorporate .exists() into it.
api.getLibraryEntries(username)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<List<Entry>, Observable<Entry>>() {
#Override
public Observable<Entry> call(List<Entry> Entries) {
return Observable.from(Entries);
}
})
.first(new Func1<Entry, Boolean>() {
#Override
public Boolean call(Entry entry) {
return entry.getId() == id;
}
})
.subscribe(
new Action1<Entry>() {
#Override
public void call(Entry entry) {
view.showEntry(entry);
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
view.showError(throwable.getMessage());
}
});
Any help is appreciated.
There is a firstOrDefault operator:
// ...
.firstOrDefault(yourDefaultValue, new Func1<Entry, Boolean>() {
#Override
public Boolean call(Entry entry) {
return entry.getId() == id;
}
})
// ...

RxJava nested Zip operation not returning

I've created this long observable with several nested observables and two Zip operators.
My issue is that the outer most Zip operator never has its function called... in this case it's Func5(...).
I am able to debug the nested Zip operator and see the Func6(...) return the Map<String, Object> but then nothing happens. No onNext, onError, or onComplete from the subscriber.
What am I missing here?
The Zip operators return an observable so I assumed nesting was ok.
Observable.zip(
UserApi.doThis(someValue)
.flatMap(new Func1<String, Observable<typeA1>>() {
#Override
public Observable<typeA1> call(String photoId) {
return UserApi.getUserPhotoData(photoId);
}
}),
UserApi.doThat(),
UserApi.doSomething(settingName0),
InstitutionApi.getThis(someValue),
Observable.zip(
InstitutionApi.getInstitutionSetting(settingName1).onErrorReturn(new Func1<Throwable, typeB1>() {
#Override
public typeB1 call(Throwable throwable) {
return new typeB1(...);
}
}),
InstitutionApi.getInstitutionSetting(settingName2).onErrorReturn(new Func1<Throwable, typeB1>() {
#Override
public typeB1 call(Throwable throwable) {
return new typeB1(...);
}
}),
InstitutionApi.getInstitutionSetting(settingName3).onErrorReturn(new Func1<Throwable, typeB1>() {
#Override
public typeB1 call(Throwable throwable) {
return new typeB1(...);
}
}),
InstitutionApi.getInstitutionContentString(stringName1).onErrorReturn(new Func1<Throwable, typeB2>() {
#Override
public typeB2 call(Throwable throwable) {
return null;
}
}),
InstitutionApi.getInstitutionSetting(settingName4).onErrorReturn(new Func1<Throwable, typeB1>() {
#Override
public typeB1 call(Throwable throwable) {
return new typeB1(...);
}
}),
InstitutionApi.getInstitutionSetting(settingName5).onErrorReturn(new Func1<Throwable, typeB1>() {
#Override
public typeB1 call(Throwable throwable) {
return new typeB1(...);
}
}),
new Func6<typeB1, typeB1, typeB1, typeB2, typeB1, typeB1, Map<String, Object>>() {
#Override
public Map<String, Object> call(typeB1 r0, typeB1 r1, typeB1 r2,
typeB2 r3, typeB1 r4, typeB1 r5) {
Map<String, Object> map = new HashMap<>();
try {
// do things
} catch (Exception e) {
return null;
}
return map;
}
}),
new Func5<typeA1, typeA2, typeA3, typeA4, Map<String, Object>, EndReturnType>() {
#Override
public EndReturnType call(typeA1 r0, typeA2 r1, typeA3 r2, typeA4 r3, Map<String, Object> r4) {
EndReturnType ert = new EndReturnType ();
// do things
return ert;
}
});
As zsxwing mentioned in the comments, one of my functions was not returning a value due to a missing onNext function.

Categories

Resources