Ich schreibe gern Code, der Method-Chaining (a.k.a. fluent style) verwendet. Leider macht da Java nicht immer mit, sei es durch veränderliche Objekte, sei es durch den unreflektierten Einsatz von static-Methoden. Ich habe mal einen Wrapper (technisch gesehen die Identity-Monade) gebastelt, der das Chaining auch in diesen Fällen erlaubt, und durch die Möglichkeit, zusätzliche Argumente anzugeben, auch die Verwendung von Methoden-Referenzen erleichtert.
Mal ein bisschen Nonsense-Code:
int i = Id.of("sdfkjsdlfj")
.call2(1, String::substring)
.call13("(%s---%d)", 42, String::format)
.peek(System.out::println)
.map(String::length)
.map(AtomicInteger::new)
.peek2(3, AtomicInteger::getAndAdd)
.map(AtomicInteger::intValue)
.get();
Die Zahlen geben immer an, wo die zusätzlichen Argumente beim Aufruf plaziert werden.
Falls jemand damit rumspielen will, hier die Implementierung:
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
public final class Id<A> {
private final A value;
private Id(A value) {
this.value = value;
}
public static <A> Id<A> of(A value) {
return new Id<>(value);
}
public A get() {
return value;
}
public <B> Id<B> map(Function<? super A, ? extends B> fn) {
return of(fn.apply(value));
}
public <B> Id<B> flatMap(Function<? super A, Id<B>> fn) {
return fn.apply(value);
}
public Id<A> peek(Consumer<? super A> consumer) {
consumer.accept(value);
return this;
}
public <B> Id<A> peek1(B b, BiConsumer<? super B, ? super A> consumer) {
consumer.accept(b, value);
return this;
}
public <B> Id<A> peek2(B b, BiConsumer<? super A, ? super B> consumer) {
consumer.accept(value, b);
return this;
}
public <B, C> Id<C> call1(B b, BiFunction<B, A, C> fn) {
return of(fn.apply(b, value));
}
public <B, C> Id<C> call2(B b, BiFunction<A, B, C> fn) {
return of(fn.apply(value, b));
}
public <B, C, D> Id<D> call12(B b, C c, TriFunction<B, C, A, D> fn) {
return of(fn.apply(b, c, value));
}
public <B, C, D> Id<D> call13(B b, C c, TriFunction<B, A, C, D> fn) {
return of(fn.apply(b, value, c));
}
public <B, C, D> Id<D> call23(B b, C c, TriFunction<A, B, C, D> fn) {
return of(fn.apply(value, b, c));
}
public interface TriFunction<A, B, C, D> {
D apply(A a, B b, C c);
}
}
Denkt ihr, das sowas praxistauglich ist?