Reputation: 7485
Exploring the new features of Java 8, I stumbled the wish to create a Consumer<X>
by chaining a Consumer<Y>
to Function<X,Y>
.
Does this make sense? And if so, how would a good (general) solution look like?
What I've tried (rather a special case by example):
Given
@FunctionalInterface
public interface PartialFunction<X, Y> {
Y apply(X x) throws Exception;
}
and
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
public class PartialFunctions {
public static <X, Y> Function<X, Optional<Y>> withOptionalResults(final PartialFunction<X, Y> funcThatThrows) {
return z -> {
try {
return Optional.of(funcThatThrows.apply(z));
} catch (final Exception e) {
return Optional.empty();
}
};
}
public static <X, Y> Consumer<X> acceptOnSuccess(final PartialFunction<X, Y> g, final Consumer<Y> c) {
return x -> withOptionalResults(x).apply(t).ifPresent(c);
}
}
I end up with a possible usage like:
files.forEach(PartialFunctions.<File, BufferedImage>acceptOnSuccess(
ImageIO::read, images::add));
However, the need for the explicit generic specification is not optimal. Hopefully there is something better?
Upvotes: 11
Views: 7426
Reputation: 61
You could try something like this?
public void lambdaChaining() {
System.out.println("\nlambda chaining:");
List<String> l = Arrays.asList("1", "22", "333", "4444", "55555", "666666", "7777777", "88888888", "999999999");
Function<String, ?> f = s -> s.length();
Consumer<String> c = s -> System.out.println(f.apply(s));;
l.forEach(c);
}
Upvotes: 2
Reputation: 1075
You could extend the Function
interface like so:
public interface ComposableFunction<T, R> extends Function<T, R> {
default Consumer<T> andThen(Consumer<R> after) {
Objects.requireNonNull(after);
return (T t) -> {after.accept(apply(t));};
}
}
And then use it regularly like so:
ComposableFunction<Throwable, String> getMessage = Throwable::getMessage;
Consumer<String> log = System.out::println;
Consumer<Throwable> logMessage = getMessage.andThen(log);
Upvotes: 5
Reputation: 1727
interface IgnoreThrowing<F,V> extends Function<F,V> {
public default V apply(F from) {
try {
return ignore(from);
} catch(Exception e) {
return null;
}
}
public V ignore(F from) throws Exception;
}
class Throwables {
public static <F,V> Function<F,V> ignore(IgnoreThrowing<F,V> f) {
return f;
}
}
static {
files.map(Throwables.ignore(ImageIO::read)).collect(...)
}
It will get better if you add a Collector that ignores nulls as input.
edit: i wrote this without syntax checking or compiling, so not totally sure about the placement of the default, and whether the compiler can successfully infer the chained function type parameters.
Upvotes: 10
Reputation: 1727
the other option (and the more general case) would be to use new Function<X,Y>(...).andThen(new Function<Y,Z>(...))
. I wouldnt mix consumers and functions, rather chain functions then feed into consumers (who optionally are also chained).
Upvotes: 0