Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 Functions 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 Functions. 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?

like image 243
syntagma Avatar asked Oct 21 '15 12:10

syntagma


People also ask

What are variable length arguments with types?

Variable-length arguments, abbreviated as varargs, are defined as arguments that can also accept an unlimited amount of data as input. The developer doesn't have to wrap the data in a list or any other sequence while using them. Let's understand the concept with the help of an example.

Which of the following function definitions allows the function to accept variable length arguments?

With Python, we can use the *args or **kwargs syntax to capture a variable number of arguments in our functions. Using *args , we can process an indefinite number of arguments in a function's position.

Which function accepts a variable number of arguments?

In mathematics and in computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments.

What is variable length argument in function?

A variable-length argument is a feature that allows a function to receive any number of arguments. There are situations where a function handles a variable number of arguments according to requirements, such as: Sum of given numbers. Minimum of given numbers and many more.


2 Answers

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);
    }

}
like image 127
Tunaki Avatar answered Nov 18 '22 15:11

Tunaki


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".

like image 21
NilsH Avatar answered Nov 18 '22 15:11

NilsH