Product::getInfo can be assigned to a Function<Product,Info> (since it's a non-static method, so it can be seen as a function that takes a Product instance and returns an Info instance). Change the declaration of functions from
List<Function> functions = new ArrayList<>();
to
List<Function<Product,Info>> functions = new ArrayList<>();
EDIT: I tested your code, and I get a different compilation error:
The type Product does not define getInfo(Object) that is applicable here.
The compilation error you got is misleading. Once I make the suggested change, the error goes away.
Answer from Eran on Stack OverflowWorking with an ArrayList of Functions in Java-8 - Stack Overflow
testing - Creating a list of functions in java - Software Engineering Stack Exchange
How do I create a List of functions in Java? - Stack Overflow
How to apply a list of functions to a value in Java 8? - Stack Overflow
Videos
The method of a java.util.function.Function object is apply. You need to call it like this:
operations.get(i).apply(initialValue)
However you use raw Function and therefore the result could be Object and you'd need to convert it to the appropriate type. Also you can't use the + (or the +=) operator with it. I'd suggest restricting the parameter types with Number:
List<Function<Number, ? extends Number>> operations = Arrays.asList(
num -> num.intValue() + 1,
num -> num.intValue() - 1,
num -> num.doubleValue() * 3.14
); // just an example list here
public double getResult() {
double result = 0.0;
for (int i = 0; i < operations.size(); i++) {
if (i == 0) {
result = operations.get(i).apply(initialValue).doubleValue();
} else {
result += operations.get(i).apply(result).doubleValue();
}
}
return result;
}
I'm not convinced I understand correctly, but I think the problem you're facing is how to call the functions in your List? The JavaDoc for the Function interface states that it has a single non-abstract method, apply() that you call to use the Function, as follows:
public double getResult(){
double result = 0.0;
for(int i = 0; i < operations.size(); i++){
if(i == 0)
result = operations.get(i).apply(initialValue);
else
result += operations.get(i).apply(result);
}
return result;
}
As an aside, that method could be tidied up a bit to make it simpler:
public double getResult() {
double result = initialValue;
//Not sure if this if-statement is a requirement, but it is to replicate
//the functionality in the question
if (operations.isEmpty()) {
return 0.0;
}
for (Operation operation : operations) {
result = operation.apply(result);
}
return result;
}
And as others have said in the comments, you should use generics when passing around your List objects (I guess you'll have to use something like List<Function<? extends Number, ? extends Number>)
In Java, this is the best you can hope for. For each new function, you must make an additional change to ensure that it gets called. While you could pass an array of Functions to prevent setting up each individual call, you still need to alter the array with each successive function created, so it is ultimately no better.
Though if you're like me, you may want to categorize these functions so that you can have a single comprehensive list. While it doesn't reduce the amount of changes you'll need to make to the code, this way I find is more organized.
public enum TestFunctions {
BUBBLE_SORT(SortingAlgorithms::bubbleSort),
QUICK_SORT(SortingAlgorithms::quickSort),
MERGE_SORT(SortingAlgorithms::mergeSort),
INSERTION_SORT(SortingAlgorithms::insertionSort);
UnaryOperator<Integer[]> function;
private TestFunctions(UnaryOperator<Integer[]> function) {
this.function = function;
}
public Integer[] call(Integer[] input) {
return this.function.apply(input);
}
}
Like this, your code can separate the function from where it's implemented. You'd still need to add the new function here, but there's no getting around this unless you used reflection. Though you could use reflection, I would strongly discourage you from doing so. My experience is that it tends to create more problems than it solves in the long run.
Good luck!
If you want to create a list of functions, that is very easy. You literally just need to create a List of Functions. Example:
import java.util.List;
import java.util.function.Function;
class Test {
private static List<Function<int[], int[]>> functions = List.of(
a -> new int[] { a[0] + a[1] },
a -> new int[] { a[0] - a[1] }
);
public static void main(String[] args) {
for (var f : functions) {
System.out.println(f.apply(new int[] { 1, 2 })[0]);
}
}
}
// 3
// -1
First, create a functional interface that represents the common signature of all the functions. Note: there may be an interface from the java.util.function package that can be used to represent the signature, so it may not be necessary to create a new interface.
// for example
@FunctionalInterface
interface MyFunction {
int f(int a, int b); // this must match the signature of each function
}
Then, create a List of that type.
List<MyFunction> functions = List.of(this::myFunction, MyClass::someOtherFunction);
You have to help Java a little for this one:
var myListOfFunctions = List.<Function<String,Integer>> of(this::myFunction, MyClass::someOtherFunction);
This would generate a List<Function<String, Integer>>.
This has just been asked a few hours ago for a Consumer... You could reduce them to a single function and apply that:
@SafeVarargs
private static <T> Function<T, T> combineF(Function<T, T>... funcs) {
return Arrays.stream(funcs).reduce(Function.identity(), Function::andThen);
}
Here's a variant on Eugene's answer, just for fun:
public static <T> Function<T, T> combine(List<Function<T, T>> functions) {
return new Object() {
Function<List<Function<T, T>>, Function<T, T>> combiner = list ->
list.size() == 1 ? list.get(0) :
list.get(0).andThen(this.combiner.apply(list.subList(1, list.size())));
}.combiner.apply(functions);
}
This creates an anonymous inner class with an attribute that is a recursive lambda. This attribute is named combiner and it's a higher-order function that takes a list of functions as an argument and returns a function as a result. This higher-order function returns the first function of the list if the list contains only one element, or applies andThen to the first function of the list, with the function that results of the recursive call to the higher-order function with the sublist of functions that starts with the second element.
The anonymous inner class is needed because recursive lambdas can only be defined as attributes of a class.
Needless to say this is way more complex than streaming the list and reducing with the Function::andThen binary operator. Besides, recursive lambdas aren't for free: they use the stack for the recursive calls.