| 

.NET C# Java Javascript Exception

10
Ich wünschte wirklich, dass es in Java mehr Unterstützung für „monadisches Zeug“ im Doppelpack gäbe. Angefangen von Pair, zu Optionals, und bis zu Streams. Pair ist natürlich langweilig, und x-mal verfügbar, aber hier mal ein Schnellschuss, wie ich mir eine Helferklasse für zwei Optionals vorstellen würde: Die Benutzung sollte ziemlich selbsterklärend sein, deshalb spare ich […]

Ich wünschte wirklich, dass es in Java mehr Unterstützung für „monadisches Zeug“ im Doppelpack gäbe. Angefangen von Pair, zu Optionals, und bis zu Streams. Pair ist natürlich langweilig, und x-mal verfügbar, aber hier mal ein Schnellschuss, wie ich mir eine Helferklasse für zwei Optionals vorstellen würde:

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>"));
 }
}

Die Benutzung sollte ziemlich selbsterklärend sein, deshalb spare ich mir Beispiele. Ansonsten ist nur noch zu erwähnen, dass ich stark in Versuchung war, die Klasse „Dopptional“ zu nennen – aber der Pun ist selbst für meine Verhältnisse zu mies…

Der Code als Gist

uncategorized
Weitere News:
Schreibe einen Kommentar:
Themen:
uncategorized
Entweder einloggen... ...oder ohne Wartezeit registrieren
Benutzername
Passwort
Passwort wiederholen
E-Mail