Conditional operations in RxJava code - java

I'm new to RxJava and often find myself resorting to solutions like the ones below for conditional operations. Here I want to chain two calls in sequence, and then return an int depending on what the outcome of the call chain is. Are there any "rxified" way to improve this code? (using blockingSingle here since I'm passing the resulting int to a legacy application to which I cannot push values as of yet)
return restApiAdapter.closePosition(request)
.flatMap(dealReference -> restApiAdapter.getDealConfirmationObservable(dealReference.getValue())
.map(dealConfirmationResponse -> {
if (dealConfirmationResponse.getDealStatus() == DealStatus.ACCEPTED) {
return SUCCESS.getValue();
} else {
return FAIL.getValue();
}
})
)
.onErrorReturn(e -> ZorroReturnValues.BROKER_SELL_FAIL.getValue())
.blockingSingle();

After moving the logic to check for ACCEPTED/REJECTED orders as suggested by #andrei-macarie, the code now looks more like this
return restApiAdapter.closePosition(request)
.flatMap(dealReference -> restApiAdapter.getDealConfirmationObservable(dealReference.getValue())
.map(dealConfirmationResponse -> SUCCESS.getValue()
)
.onErrorReturn(e -> FAIL.getValue())
.blockingSingle();

Related

Project Reactor conditional execution of independent verification step

I can't figure it out, how to do this method without the if/else:
public Mono<Token> doAuthorization(InputDto dto) {
if (isXStepNeeded(dto)) {
return doXStep(dto)
.then(doYStep(dto.getRfid()));
} else {
return doYStep(dto.getRfid());
}
}
private boolean isXStepNeeded(InputDto dto) {
//simple non blocking check on the dto
}
private Mono<OtherDto> doXStep(InputDto dto) {
//checking something and returning Mono.error() if it fails
}
private Mono<Token> doYStep(String tokenUid) {
//...
}
As you can see, the X and Y steps are independent of each other. Is there a nice, readable way of writing doAuthorization that does not use if/else and I only have to write down doYStep() once?
There is no way to do this without an if else while keeping it readable. Some options to do while keeping it readable include using "ternary operator" and new "switch case" introduced in Java 14.
Reduce it to one line using ternary operator:
return isXStepNeeded(dto) ? doXStep(dto).then(doYStep(dto.getRfid())) : doYStep(dto.getRfid());
Or use the new switch case:
return switch (Boolean.toString(isXStepNeeded(dto))) {
case "true" -> doXStep(dto).then(doYStep(dto.getRfid()));
default -> doYStep(dto.getRfid());
};
EDIT:
Since you don't want to write doYStep twice, you can do:
return Mono.just(isXStepNeeded(dto))
.filter(b -> b)
.flatMap(b -> doXStep(dto))
.then(doYStep(dto.getRfid()));

Return a list from list.forEach with Java Streaming API

I have a POJO:
class MyObject {
private Double a;
private String b;
//constructor, getter + setter
}
Some function is creating a list of this POJO. Some values for a might be null, so I want to replace them with 0.0. At the moment I am doing it like this.
public List<MyObject> fetchMyObjects(Predicate predicate) {
List<MyObject> list = getMyListsOfTheDatabase(predicate);
list
.forEach(myObject -> {
if (myObject.getA() == null) {
myObject.setA(0.0);
}
});
return list;
}
Is there a way to integrate the forEach in the return? Something like
return list
.stream()
.someStatement();
It's not about, if this is the best place to convert the nulls to zero, but rather a questions to better understand the streaming api.
Use the peek function
Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
public List<MyObject> fetchMyObjects(Predicate predicate) {
return getMyListsOfTheDatabase(predicate)
.stream()
.peek(it -> if(it.getA() == null) it.setA(0.0))
.collect(Collectors.toList());
}
While others have been happy to answer your question as it stands, allow me to step a step back and give you the answer you didn’t ask for (but maybe the answer that you want): You don’t want to do that. A stream operation should be free from side effects. What you are asking for is exactly a stream operation that has the side effect of modifying the original objects going into the stream. Such is poor code style and likely to confuse those reading your code after you.
The code you already have solves your problem much more nicely than any combined stream pipeline.
What you may want to have if you can modify your POJO is either a constructor that sets a to 0 if null was retrieved from the database, or method that does it that you may call from list.forEach:
list.forEach(MyObject::setAToZeroIfNull);
It's not about, if this is the best place to convert the nulls to
zero, but rather a questions to better understand the streaming api.
That’s fair. In any case I will let this answer stand for anyone else popping by.
You can't return the same List instance with a single statement, but you can return a new List instance containing the same (possibly modified) elements:
return list.stream()
.map(myObject -> {
if (myObject.getA() == null) {
myObject.setA(0.0);
}
return myObject;
})
.collect(Collectors.toList());
Actually you should be using List::replaceAll:
list.replaceAll(x -> {
if(x.getA() == null) x.setA(0.0D);
return x;
})
forEach doesn't have a return value, so what you might be looking for is map
return list
.stream()
.map(e -> {
if (e.getA() == null) e.setA(0d);
return e;
})
.whateverElse()...
The following would be fine:
list.stream()
.filter(obj -> obj.getA() == null)
.forEach(obj -> obj.setA(0.0));
return list;
However in your case just returning a Stream might be more appropriate (depends):
public Stream<MyObject> fetchMyObjects(Predicate predicate) {
return getMyListsOfTheDatabase(predicate);
}
public Stream<MyObject> streamMyObjects(List<MyObject> list) {
return list.stream()
.peek(obj -> {
if (obj.getA() == null) {
obj.setA(0.0);
}
});
}
I personally never used peek, but here it corrects values.
On code conventions, which are more string in the java community:
Indentation: Java took 4 as opposed to C++'s 3 as more separate methods,
and less indentation was expected. Debatable but okay.
For generic type parameters often a single capital like T, C, S.
For lambda parameters short names, often a single letter, hence I used obj.

rxjava2 - if else on Maybe

I am looking for what is the recommended practice in rxjava2 to handle a case where one flowable leads to conditional behaviors.
More concretely, I have a Maybe<String> for which I want to Update the String on the database if the String exists or, if it doesn't exists I want to create a new String and save it on the database.
I thought of the below but obviously it is not what I am looking for:
Maybe<String> source = Maybe.just(new String("foo")); //oversimplified source
source.switchIfEmpty(Maybe.just(new String("bar"))).subscribe(result ->
System.out.println("save to database "+result));
source.subscribe(result -> System.out.println("update result "+result));
The above obviously produces
save to database foo
update result foo
I tried also the below which gives the expected result but still feel it's... weird.
Maybe<String> source = Maybe.just(new String("foo")); //oversimplified source
source.switchIfEmpty(Maybe.just(new String("bar")).doOnSuccess(result ->
System.out.println("save to database "+result))).subscribe();
source.doOnSuccess(result -> System.out.println("update result "+result)).subscribe();
How can I have an action for when the result exists and when it doesn't exists? How is that use case supposed to be handled in rxjava2?
Update 01
I tried the below and it looks cleaner than what I came up with above. Note sure it is recommended rxjava2 practice however...
Maybe.just(new String("foo"))
.map(value -> Optional.of(value))
.defaultIfEmpty(Optional.empty())
.subscribe(result -> {
if(result.isPresent()) {
System.out.println("update result "+result);
}
else {
System.out.println("save to database "+"bar");
}
});
You have the isEmpty() operator that will return you Boolean if the Maybe source is empty or not, and then you can flatMap it and write a if else statement depending on that Boolean
This is a common pattern in our code as well, though in our case the choices are themselves async. You can't get quite the right semantic by simply composing flatMapX and switchIfEmpty (in either order), so I am curious why this isn't part of the API.
Here's what we're doing for now (this example for when the 2 options are both Completables, we have similar things for the other types as well):
public static <T> Completable flatMapCompletable(Maybe<T> target,
#ClosureParams(FirstParam.FirstGenericType.class)
Closure<? extends CompletableSource> completableSupplier,
Supplier<CompletableSource> emptySupplier) {
Maybe<T> result = target.cache();
return result.isEmpty().flatMapCompletable(empty -> {
if (empty) {
return emptySupplier.get();
} else {
return result.flatMapCompletable(completableSupplier::call);
}
});
}
We're using Groovy, so we package these up as extension methods. I'm not thrilled with the need to use cache() so I'm wondering if there is a better alternative. From looking at the code, an operator which basically combines flatMapX and switch looks like it wouldn't be too hard (but I feel like I'm missing something).
Try something like this. checkDB can return a Maybe or Single or whatever which emits either an optional or a wrapper Object.
checkDB(String)
.flatMap(s -> {
if (s.isPresent()) {
return updateDB(s.get());
} else {
return insertDB("new String");
}
})
There is an solution using the flatMap call with 3 params
fun addOrUpdate(message: LocalMessage): Single<LocalMessage> {
return getById(message.id) // returns Maybe
.flatMap(
Function {
update(message) // onSuccess update call returns Single
},
Function {
Single.error(it) // onError
},
Callable {
add(message) // onComplete add call returns Single
}
)
}
}
Or shorter version
fun addOrUpdate(message: LocalMessage): Single<LocalMessage> {
return getById(message.id) // returns Maybe
.flatMap(
{
update(message) // onSuccess update call returns Single
},
{
Single.error(it) // onError
},
{
add(message) // onComplete add call returns Single
}
)
}
}

RxJava: How to conditionally apply Operators to an Observable without breaking the chain

I have a chain of operators on an RxJava observable. I'd like to be able to apply one of two operators depending on a boolean value without "breaking the chain".
I'm relatively new to Rx(Java) and I feel like there's probably a more idiomatic and readable way of doing this than my current approach of introducing a temporary variable.
Here's a concrete example, buffering items from an observable if a batch size field is non-null, otherwise emitting a single batch of unbounded size with toList():
Observable<Item> source = Observable.from(newItems);
Observable<List<Item>> batchedSource = batchSize == null ?
source.toList() :
source.buffer(batchSize);
return batchedSource.flatMap(...).map(...)
Is something like this possible? (pseudo-lambdas because Java):
Observable.from(newItems)
.applyIf(batchSize == null,
{ o.toList() },
{ o.buffer(batchSize) })
.flatMap(...).map(...)
You can use compose(Func1) to stay in-sequence but do custom behavior
source
.compose(o -> condition ? o.map(v -> v + 1) : o.map(v -> v * v))
.filter(...)
.subscribe(...)
You can also use filter operator with defaultIfEmpty if you wish to emit single value or switchIfEmpty if you wish to emit multiple values using another Observable.
val item = Observable.just("ABC")
item.filter { s -> s.startsWith("Z") }
.defaultIfEmpty("None")
.subscribe { println(it) }

If not null - java 8 style

Java 8 presents Optional class.
Before (Java 7):
Order order = orderBean.getOrder(id);
if (order != null) {
order.setStatus(true);
pm.persist(order);
} else {
logger.warning("Order is null");
}
So on Java 8 style:
Optional<Order> optional = Optional.ofNullable(orderBean.getOrder(id));
optional.ifPresent( s -> {
s.setStatus(true);
pm.persist(s);
//Can we return from method in this place (not from lambda) ???
});
//So if return take place above, we can avoid if (!optional.isPresent) check
if (!optional.isPresent) {
logger.warning("Order is null");
}
Is it correct to use Optional in this case? Can anyone propose a more convenient way in Java 8 style?
Unfortunately, the ifPresentOrElse method you're looking for will be added only in JDK-9. Currently you can write your own static method in your project:
public static <T> void ifPresentOrElse(Optional<T> optional,
Consumer<? super T> action, Runnable emptyAction) {
if (optional.isPresent()) {
action.accept(optional.get());
} else {
emptyAction.run();
}
}
And use like this:
Optional<Order> optional = Optional.ofNullable(orderBean.getOrder(id));
ifPresentOrElse(optional, s -> {
s.setStatus(true);
pm.persist(s);
}, () -> logger.warning("Order is null"));
In Java-9 it would be easier:
optional.ifPresentOrElse(s -> {
s.setStatus(true);
pm.persist(s);
}, () -> logger.warning("Order is null"));
//Can we return from method in this plase (not from lambda) ???
Lambdas do not implement "non-local return" semantics, therefore the answer is no.
Generally, since you need side-effectful action in both the case where the value is present and not, a branching point in the code is essential—whether you wrap it in some fancy API or not. Also, FP generally helps improve referentially transparent transformations (i.e., code built around pure functions) and not side effects, so you won't find much benefit by going through the Optional API.

Categories

Resources