4

Let's say I have a method that applies multiple functions to a value.

Example usage:

String value = "a string with numb3r5";
Function<String, List<String>> fn1 = ...
Function<List<String>, String> fn2 = ...
Function<String, List<Integer>> fn3 = ...

InputConverter<String> converter = new InputConverter<>(value);
List<Integer> ints = converter.convertBy(fn1, fn2, fn3);

Is it possible to make it apply multiple functions with various inputs and return values?

I've tried using wildcards, but this doesn't work.

public class InputConverter<T> {
    private final T src;

    public InputConverter(T src) {
        this.src = src;
    }

    public <R> R convertBy(Function<?, ?>... functions) {
        R value = (R) src;

        for (Function<?, ?> function : functions)
            value = (R) function.apply(value);
                                       ^^^^^
        return value;
    }
}
Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
Bart
  • 162
  • 1
  • 5
  • 12

1 Answers1

2

You can use a chain on Function like the following

Function<String, List<Integer>> functionChain = fn1.andThen(fn2).andThen(fn3);

You can achieve nearly the same thing by using raw types

@SuppressWarnings({"unchecked", "rawtypes"})
public <R> R convertBy(Function... functions) {
    Function functionsChain = Function.identity();

    for (Function function : functions) {
        functionsChain = functionsChain.andThen(function);
    }

    return (R) functionsChain.apply(src);
}

Otherwise, the only other I see is to use the same pattern as Optional or Stream like suggested in the comments

List<Integer> fileInputStreams = converter.convertBy(fn1)
        .convertBy(fn2)
        .convertBy(fn3)
        .get();

// with this implementation

public static class InputConverter<T> {
    private final T src;

    public InputConverter(T src) {
        this.src = src;
    }

    public <R> InputConverter<R> convertBy(Function<T, R> function) {
        return new InputConverter<>(function.apply(src));
    }

    public T get() {
        return src;
    }
}

Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
  • Is it possible to achieve without the use of raw types? – Bart Apr 03 '21 at 23:02
  • I found a very similar answer before posting this question: https://stackoverflow.com/a/44521687/10430194 The solution is very elegant. Unfortunately this only works when the return types are consistent. – Bart Apr 03 '21 at 23:04
  • 2
    @Bart I have added maybe a solution that could be suitable, I'm not sure it is possible to do it full generics here, as the types to infer are dynamic, and Java is not the perfect language for this kind of flexibility – Yassin Hajaj Apr 03 '21 at 23:19
  • 1
    That's a very thorough answer, my friend. I combined your answer with a functional approach and I'm returning something like this `return (R) Arrays.stream(functions).reduce(Function.identity(), Function::andThen).apply(src);`. I'm marking yours as the best answer. Thank you! – Bart Apr 05 '21 at 09:30