1

how can i use Stream for Function[]? My solution looks very ugly plus i know that this can be done by using reduce().

class A {   
    static void run() {
        Integer a = applyConversions(3, x -> x * 3, x -> x + 4);                  // output: 13

        String b = applyConversions("One", s -> "Two " + s, String::toUpperCase); // output: OneTwo
    }

    static <T> T applyConversions(T value, Function<T, T>... conversions) {
        /*
        todo: apply each conversion function to 'value'

        ==>My Ugly solution<==
        final Object[] valueHelper = {null};
        valueHelper[0] = value;
        Stream.of(conversions).forEach(s-> valueHelper[0] = s.apply((T)valueHelper[0]));
        return (T)valueHelper[0];
        */
        return value;
    }
}

I can change only signature and body of 'applyConversions' method

Naman
  • 27,789
  • 26
  • 218
  • 353
Wuzu
  • 13
  • 3

1 Answers1

2

You seem to want to reduce the functions using andThen. Assuming conversions can't be empty, you can do:

static <T> T applyConversions(T value, Function<T, T>... conversions) {
    return Arrays.stream(conversions).reduce(Function::andThen).get().apply(value);
}

If it can be empty, you need to decide what to return when it is empty.

return Arrays.stream(conversions).reduce(Function::andThen)
        .map(x -> x.apply(value)).orElse(someDefaultValue);

If you want it to use value as the default value, you do orElse(Function.identity()) instead of getting the optional.

return Arrays.stream(conversions)
   .reduce(Function::andThen).orElse(Function.identity())
   .apply(value);
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • `Function.identity` can be used with the overloaded `reduce` API. – Naman Oct 27 '20 at 02:32
  • 1
    You can fuse your first and third solution to one function, using `.reduce(Function::andThen).orElse(Function.identity())`, having the advantages of both, not combining existing functions with the identity function, but handling an empty array gracefully. Side note, this method is a candidate for `@SafeVarargs`. Its further worth noting that combining functions this way creates an unbalanced tree, so this method should not be used with overly long arrays, as evaluating the combined may risk a `StackOverflowError`. – Holger Oct 27 '20 at 07:42
  • @Holger Is "combining existing functions with the identity function" somehow _bad_? – Sweeper Oct 27 '20 at 07:45
  • 2
    I’d say, for a temporary function, the additional memory consumption doesn’t matter. Still, the result is an unbalanced tree whose evaluation has to traverse it. Such a throwaway function has little chances to get entirely inlined, so it may have an effect on the performance. There’s also a slightly higher risk of a stack overflow. And giving, how the Hotspot JVM works, with its default max inlining threshold of nine, one more function can have a surprisingly bad effect on the performance. Besides that, why did you keep the first version, when not for that reason? – Holger Oct 27 '20 at 07:49
  • @Holger The first version was meant as a fail-fast, to catch situations where `conversions` is unexpectedly empty. I wish I can think about problems as deeply as you do! :-) – Sweeper Oct 27 '20 at 07:52
  • 1
    It’s a matter of experience. I already discussed the implication of combining functions via reduction in [this answer](https://stackoverflow.com/a/59885137/2711488). It combines `Consumer` instances, but the logic is the same for `Function`. – Holger Oct 27 '20 at 08:08