0

I want to create a list of functions that I can iterate over and run in sequence.

EDIT: they should be capable of receiving arguments and returning values.

For a dummy example, in Python I can do something like this:

def fn1(s: str) -> int:
    return len(s)

def fn2(s: str) -> int:
    return len(s) + 5

list_of_fns = [
    fn1,
    fn2,
]

results = list(map(lambda fn: fn('hello'), list_of_fns))  # [5, 10]

How can I do the equivalent in Java? Say I have n static methods, how can I create an ArrayList of those methods, then iterate over them and run them in order?

2
  • 1
    ArrayList of Consumer ? In other words ArrayList<Consumer> Commented Jan 24, 2021 at 12:03
  • or a list of Runnable, since the functions do not appear to take any arguments Commented Jan 24, 2021 at 12:08

2 Answers 2

2

First, make sure that the methods you want to put into the list are of the same function type. Roughly speaking, this means that they accept the same number of parameters, and their parameter types and return types are all compatible with each other. If they aren't, then you don't really have a way of calling them afterwards, so that's not very useful.

Then, find a functional interface that represents your function type. Create a List of that interface. There are many built-in ones. For your python examples, they all take a String and return an int, so a ToIntFunction<String> is suitable.

Assuming that staticMethod1, staticMethod2 and staticMethod3 are static methods declared in SomeClass, you can do:

List<ToIntFunction<String>> myMethods = List.of(
    SomeClass::staticMethod1, SomeClass::staticMethod2, SomeClass::staticMethod3
);

To run them you just need to get a ToIntFunction<String> from the list (e.g. by looping), then call the applyAsInt method:

for (ToIntFunction<String> method : myMethods) {
    method.applyAsInt("hello");
}

Note that a different functional interface could have a different name for the method that you need to call to in order to run it.


If you can't find a suitable functional interface for your function type in the JDK, you can make one yourself. It's just an interface with a single method. For example, here is one for methods that take 4 ints and return nothing:

interface IntConsumer4 {
    void accept(int i, int j, int k, int n);
}
Sign up to request clarification or add additional context in comments.

5 Comments

Hi @Sweeper, many thanks for your help here! I should've been more explicit with my question - what if there were arguments and return values?
@Andy Then you would use another functional interface. For example, Function<T, U> is for functions that accept a T and return a U. You could always create your own if you can't find one in the JDK as I demonstrated. I suggest you read up on functional interfaces, method references, and lambda expressions. These are very related topics.
Perfect, I assumed this is what you were getting at but wanted to clarify. I'll read up on this. Many thanks again :)
I don't think there is a built in one for float accept(String a, String b), but you can use BiFunction<String, String, Float> if you don't mind the wrapper type. @Andy
@Andy updated the answer to match the question edit.
2

Sure it is possible.

An example shows usage of Consumer and Supplier, populating the lists of functions with method references and invoking the functions:

import java.util.*;
import java.util.function.*;

public class MyClass {
    
    // consumers
    static void foo(String s) {
        System.out.println("foo: " + s);
    }
    
    static void bar(String s) {
        System.out.println("bar: " + s);
    }
    
    // suppliers
    static int getOne() {
        return 1;
    }
    
    static Integer getMin() {
        return Integer.MIN_VALUE;
    }

    public static void main(String...aaa) {
        List<Consumer<String>> funs = Arrays.asList(MyClass::foo, MyClass::bar);
        funs.forEach(f -> f.accept("called from list"));
        
        List<Supplier<Integer>> sups = Arrays.asList(MyClass::getOne, MyClass::getMin);
        sups.stream().map(Supplier::get).forEach(System.out::println);
    } 
}

Output:

foo: called from list
bar: called from list
1
-2147483648

2 Comments

This is great. I realise this is a modification to the question, but how might I combine the Consumer and Supplier and (1) provide an argument, like "called from list", and (2) collect the return values?
Do you want to combine diferent types of functions in the same collection? What for? The collection would have to be raw and you'll need to verify the types of the specific elements inside collection.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.