Skip to content

Instantly share code, notes, and snippets.

@DanielGronau
Created February 25, 2019 21:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DanielGronau/a385f7d20c53afaf39e3c5c0810ccec1 to your computer and use it in GitHub Desktop.
Save DanielGronau/a385f7d20c53afaf39e3c5c0810ccec1 to your computer and use it in GitHub Desktop.
Helper class for dealing with 2 Optionals simultaneously
package dgronau;
import java.util.Objects;
import java.util.Optional;
import java.util.function.*;
public class Optional2<A, B> {
private final Optional<A> first;
private final Optional<B> second;
private Optional2(Optional<A> first, Optional<B> second) {
this.first = Objects.requireNonNull(first);
this.second = Objects.requireNonNull(second);
}
public static <A, B> Optional2<A, B> empty() {
return of(Optional.empty(), Optional.empty());
}
public static <A, B> Optional2<A, B> of(Optional<A> first, Optional<B> second) {
return new Optional2<>(first, second);
}
public static <A, B> Optional2<A, B> ofNullable(A a, B b) {
return new Optional2<>(Optional.ofNullable(a), Optional.ofNullable(b));
}
public static <A> Optional<A> unify(Optional2<A, A> optional2, BinaryOperator<A> op) {
return unify(optional2.first, optional2.second, op);
}
public static <A> Optional<A> unify(Optional<A> first, Optional<A> second, BinaryOperator<A> op) {
return first.map(
a -> second.map(
b -> op.apply(a, b)).orElse(a))
.map(Optional::of)
.orElse(second);
}
public Optional<A> first() {
return first;
}
public Optional<B> second() {
return second;
}
public Optional2<B, A> swap() {
return of(second, first);
}
public <C> Optional<C> map(BiFunction<A, B, C> fn) {
return first.flatMap(
a -> second.map(
b -> fn.apply(a, b)));
}
public <C> Optional2<C, B> mapFirst(BiFunction<A, B, C> fn) {
return of(map(fn), second);
}
public <C> Optional2<C, B> mapFirst(Function<A, C> fn) {
return of(first.map(fn), second);
}
public <C> Optional2<A, C> mapSecond(BiFunction<A, B, C> fn) {
return of(first, map(fn));
}
public <C> Optional2<A, C> mapSecond(Function<B, C> fn) {
return of(first, second.map(fn));
}
public <C> Optional<C> flatMap(BiFunction<A, B, Optional<C>> fn) {
return first.flatMap(
a -> second.flatMap(
b -> fn.apply(a, b)));
}
public <C> Optional2<C, B> flatMapFirst(BiFunction<A, B, Optional<C>> fn) {
return Optional2.of(flatMap(fn), second);
}
public <C> Optional2<C, B> flatMapFirst(Function<A, Optional<C>> fn) {
return Optional2.of(first.flatMap(fn), second);
}
public <C> Optional2<A, C> flatMapSecond(BiFunction<A, B, Optional<C>> fn) {
return Optional2.of(first, flatMap(fn));
}
public <C> Optional2<A, C> flatMapSecond(Function<B, Optional<C>> fn) {
return Optional2.of(first, second.flatMap(fn));
}
public <C, D> Optional2<C, D> biFlatMap(BiFunction<A, B, Optional2<C, D>> fn) {
return first.flatMap(
a -> second.map(
b -> fn.apply(a, b)
)).orElseGet(Optional2::empty);
}
public Optional2<A, B> filter(BiPredicate<A, B> predicate) {
return map(predicate::test).orElse(false) ? this
: Optional2.of(Optional.empty(), Optional.empty());
}
public Optional2<A, B> filterFirst(BiPredicate<A, B> predicate) {
return map(predicate::test).orElse(false) ? this
: Optional2.of(Optional.empty(), second);
}
public Optional2<A, B> filterFirst(Predicate<A> predicate) {
return Optional2.of(first.filter(predicate), second);
}
public Optional2<A, B> filterSecond(BiPredicate<A, B> predicate) {
return map(predicate::test).orElse(false) ? this
: Optional2.of(first, Optional.empty());
}
public Optional2<A, B> filterSecond(Predicate<B> predicate) {
return Optional2.of(first, second.filter(predicate));
}
public boolean bothPresent() {
return first.isPresent() && second.isPresent();
}
public boolean bothEmpty() {
return !first.isPresent() && !second.isPresent();
}
public Optional2<A, B> ifBothPresent(BiConsumer<A, B> consumer) {
first.ifPresent(
a -> second.ifPresent(
b -> consumer.accept(a, b)));
return this;
}
public Optional2<A, B> ifBothEmpty(Runnable runnable) {
if (!first.isPresent() && !second.isPresent()) {
runnable.run();
}
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Optional2<?, ?> optional2 = (Optional2<?, ?>) o;
return first.equals(optional2.first) &&
second.equals(optional2.second);
}
@Override
public int hashCode() {
return Objects.hash(first, second);
}
@Override
public String toString() {
return String.format("Optional2[%s,%s]",
first.map(Object::toString).orElse("<empty>"),
second.map(Object::toString).orElse("<empty>"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment