Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8: Chain Consumer<Y> to Function<X,Y>

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?

like image 676
Jens Piegsa Avatar asked Dec 13 '13 16:12

Jens Piegsa


2 Answers

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.

like image 98
aepurniet Avatar answered Oct 01 '22 08:10

aepurniet


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);
like image 24
david-hoze Avatar answered Oct 01 '22 10:10

david-hoze