简体   繁体   中英

Create a method that accepts variable length of Function arguments with possibly different types

Suppose I have a string: String s = "1,2,3,4,5,6" . I would like to create a method combineFunctions() that would take a variable length sequence of Function s as an argument and apply all of the operations in that order.

The functions may have different <T,U> types.

Example uses of such a function would be the following:

Combine<String> c = new Combine<>(s);
List<String> numbers = c.combineFunctions(splitByComma);
Integer max = c.combineFunctions(splitByComma,convertToInt, findMax);

What I have tried (the <U> here is not of much use here):

public <U> void combineFunctions(
        Function<? extends Object, ? extends Object>... functions) {

}

But I am stuck at getting type of last one of the Function s. I was also thinking about a recursive approach but the varargs parameter has to be the last one.

Would it be possible to implement such method in Java?

The problem with such a function is that you loose all compile-time type checking and casting is necessary.

This would be an implementation, using andThen to combine functions together. This looks ugly because of all the casting and I'm not sure you can do it more properly. Notice also that this requires the creation of 2 Stream pipelines when only 1 is really necessary.

public static void main(String[] args) throws Exception {
    String str = "1,2,3,4,5,6";
    Function<Object, Object> splitByComma = s -> ((String) s).split(",");
    Function<Object, Object> convertToInt = tokens -> Stream.of((String[]) tokens).map(Integer::valueOf).toArray(Integer[]::new);
    Function<Object, Object> findMax = ints -> Stream.of((Integer[]) ints).max(Integer::compare).get();
    Integer max = (Integer) combineFunctions(splitByComma, convertToInt, findMax).apply(str);
    System.out.println(max);
}

@SafeVarargs
private static Function<Object, Object> combineFunctions(Function<Object, Object>... functions) {
    return Arrays.stream(functions)
                 .reduce(Function::andThen)
                 .orElseThrow(() -> new IllegalArgumentException("No functions to combine"));
}

To match the code in your question, you could wrap this into a class like this:

public class Combiner<R> {

    private Object input;

    public Combiner(Object input) {
        this.input = input;
    }

    @SuppressWarnings("unchecked")
    @SafeVarargs
    public final R combineFunctions(Function<Object, Object>... functions) {
        return (R) Arrays.stream(functions)
                         .reduce(Function::andThen)
                         .orElseThrow(() -> new IllegalArgumentException("No functions to combine"))
                         .apply(input);
    }

}

The example in your question is quite easily solved by using the functional-style Streams . The "functional" approach to solving this is by using sequences of map operations, each step transforming the elements to a different type, and then optionally reducing/collecting the result.

For instance

String str = "1,2,3,4,5,6,7";
int max = Arrays.stream(str.split(",")).mapToInt(Integer::parseInt).max().orElse(0)

The same pattern would apply for other types of "function combinations".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM