Reputation: 11855
JDK 8:
I'm validating input request. Is there a way to nicely chain these optionals? Or some other way to make it look a bit nicer and get rid off too many if optional checks? kinda looks ugly.
In REST controller class:
{
...
Optional<ResponseEntity<?>> operationError = requestValidator.handleOperationError(bodyParams, myHandler, serviceInstance, null);
if( operationError.isPresent()){
return operationError.get();
}
Optional<ResponseEntity<?>> otherError = requestValidator.checkRequestOther(serviceInstance, bodyParams, instanceAction);
if( otherError.isPresent()){
return otherError.get();
}
Optional<ResponseEntity<?>> someWeirdError = requestValidator.checksomeWeird(serviceInstance, instanceAction);
if( someWeirdError.isPresent()){
return someWeirdError.get();
}
Optional<ResponseEntity<?>> conflictError = requestValidator.conflictExists(instanceAction,serviceInstance);
if( conflictError.isPresent()){
return conflictError.get();
}
//If All fine start doing business logic
}
Upvotes: 1
Views: 277
Reputation: 3993
I'm just going to start by saying: cleanest solution would require a better abstraction than Optional
provides, because it's main purpose is to represent a missing value, not validation output.
However, what you want to achieve is still possible with use of Optional::or
added since JDK 9:
Optional<ResponseEntity<?>> validationError =
requestValidator.handleOperationError(bodyParams, myHandler, serviceInstance, null)
.or(() -> requestValidator.checkRequestOther(serviceInstance, bodyParams, instanceAction))
.or(() -> requestValidator.checkSomeWeird(serviceInstance, instanceAction))
.or(() -> requestValidator.conflictExists(instanceAction,serviceInstance));
if( validationError.isPresent() ){
return validationError.get();
}
If you can't use Java 9, you will have to roll your own type, something like this:
public final class Error<T> {
private T value;
private Error(T value) {
this.value = value;
}
public static Error withValue(T value) {
requireNonNull(value);
return new Error(value);
}
public static <T> Error<T> start() {
return new Error(null);
}
public static <T> Error<T> startWith(Supplier<T> firstCheck) {
requireNonNull(firstCheck);
return new Error(firstCheck.get());
}
public Error<T> thenCheck(Supplier<Error<? extends T>> nextCheck) {
requireNonNull(nextCheck);
return isPresent() ? this : requireNonNull(nextCheck.get());
}
public boolean isPresent() {
return value != null;
}
public T get() {
return value; // I think no need to be particularly fancy here, null means "No Error"
}
}
Then, assuming your validator now returns Error
instances:
Error<ResponseEntity<?>> validationError =
requestValidator.handleOperationError(...)
.thenCheck(() -> requestValidator.checkRequestOther(...))
.thenCheck(() -> requestValidator.checkSomeWeird(...))
.thenCheck(() -> requestValidator.conflictExists(...));
if (validationError.isPresent()) {
return validationError.get();
}
Upvotes: 3
Reputation: 5394
If you need a strict Java 8 solution, you can create an helper class:
public class OptionalHelper {
public static <T> Optional<T> findFirstPresent(Supplier<Optional<T>>... optionals) {
return Arrays.stream(optionals)
.map(Supplier::get)
.filter(Optional::isPresent)
.findFirst()
.orElse(Optional.empty());
}
}
And use it like this:
Optional<ResponseEntity<?>> validationError = OptionalHelper.findFirstPresent(
() -> requestValidator.handleOperationError(bodyParams, myHandler, serviceInstance, null),
() -> requestValidator.checkRequestOther(serviceInstance, bodyParams, instanceAction),
() -> requestValidator.checkSomeWeird(serviceInstance, instanceAction),
() -> requestValidator.conflictExists(instanceAction, serviceInstance));
if (validationError.isPresent()) {
return validationError.get();
}
Upvotes: 1
Reputation: 3557
Yes, you can chain the Optional
values in a Stream
, but I would only do this if getting the Optional
s is inexpensive, as they are all evaluated even when the first one is present:
Optional<ResponseEntity<?>> operationError =
requestValidator.handleOperationError(bodyParams, myHandler, serviceInstance, null);
Optional<ResponseEntity<?>> otherError =
requestValidator.checkRequestOther(serviceInstance, bodyParams, instanceAction);
Optional<ResponseEntity<String>> some409Error =
requestValidator.checkSome409(serviceInstance, instanceAction);
Optional<ResponseEntity<?>> conflictError =
requestValidator.conflictExists(instanceAction,serviceInstance);
Optional<ResponseEntity<String>> anyError =
Stream.of(operationError, otherError, some409Error, conflictError)
.filter(Optional.isPresent)
.map(Optional::get)
.findFirst();
if (anyError.isPresent) {
return anyError.get();
}
A Java 8 Solution that evaluates lazily:
@SafeVarargs
public static <E> Optional<E> any(final Supplier<Optional<E>>... optionalSuppliers) {
return Stream.of(optionalSuppliers)
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
}
And the callsite:
Optional<ResponseEntity<?>> anyError = any(
() -> requestValidator.handleOperationError(bodyParams, myHandler, serviceInstance, null),
() -> requestValidator.checkRequestOther(serviceInstance, bodyParams, instanceAction),
() -> requestValidator.checkSome409(serviceInstance, instanceAction),
() -> requestValidator.conflictExists(instanceAction, serviceInstance)
);
if (anyError.isPresent) {
return anyError.get();
}
Upvotes: 1