How to pass a function as an parameter to another

2019-07-22 03:59发布

问题:

I would like pass a function as an parameter to another function. For example:

void myFunction(boolean coondition, void function())
{
   if(condition) {
     function();
   }
}

It this possible in Java 8?

回答1:

No, you can't pass methods.

But there is a simple workaround: pass a Runnable.

void myFunction(boolean coondition, Runnable function)
{
   if(condition) {
     function.run();
   }
}

and call it like this: (using the old syntax)

myFunction(condition, new Runnable() {
    @Override
    public void run() {
        otherFunction();
    }
});

or using the new lambda syntax in Java 8 (which is mostly shorthand for the above):

myFunction(condition, () -> {otherFunction();}}


回答2:

Yes, it is possible. Assuming your function does nothing, just pass a Runnable.

void myFunction(boolean condition, Runnable function)
{
   if(condition) {
     function.run();
   }
}

Assuming you have a function named function as such (void can be replaced by a return type, which will not be used):

private void function() {/*whatever*/}

you can call it like this using lambda expressions

myFunction(true, () -> function());

or like this in Java 1.1-1.7

myFunction(true, new Runnable(){public void run(){function();}});


回答3:

In Java, quite everything is an object (but primitive types). But you can use functional interfaces to handle functions. Before Java 8, there was already use-cases where functional interfaces were used often through the instantiation of anonymous classes:

  • callbacks/listeners like ActionListener
  • or Comparator to sort collections.

If you want to pass a function as a parameter, you have many ways to express that:

Let's say you want to apply some function (e.g. square a value) if some condition is met on the initial value (e.g. this is an even number):

// Return the square of a number 
static Function<Integer, Integer> square = new Function<Integer, Integer>() {
    @Override
    public Integer apply(Integer t) {
        return t*t;
    }       
};

// Return true is the parameter is an even number
static Predicate<Integer> isEven = new Predicate<Integer>() {
    @Override
    public boolean test(Integer t) {
        return (t % 2) == 0;
    }
};

// A generic function that prints for each x of the list
// xtrans(x) only if pred(x) is true.
public static <T, R> void printIf(Predicate<T> pred, Function<T, R> xtrans, List<T> xs) {
    for (T x : xs) {
        if (pred.test(x)) {
            System.out.print(xtrans.apply(x));      
            System.out.print(" ");      
        }
    }
}

You can test it:

public static void main(String[] args) {
    List<Integer> ints = IntStream.range(0, 10)
         .boxed()
         .collect(Collectors.toList());
    printIf(isEven, square, ints);
}

=> 0 4 16 36 64

And this can also be written with lambdas:

public static void main(String[] args) {
    List<Integer> ints = IntStream.range(0, 10).boxed().collect(Collectors.toList());

    Predicate<Integer> even = x -> (x % 2) == 0;
    Function<Integer, Integer> square = x -> x*x;

    printIf(even, square, ints);
}

Or directly:

    printIf(x -> (x % 2)==0, x -> x*x, ints);

And you can also use member methods as functions, provided they have a functional interface signature.

// Wrap a random integer between 0 and 10
public class Foo {
    static Random gen = new Random();
    int bar = gen.nextInt(10);
    public void println() { System.out.println(bar); }
    public int getBar() { return bar; }
}

// Execute doTo(x) if pred(x) is true
public static <T> void doToIf(Predicate<T> pred, Consumer<T> doTo, T x) {
    if (pred.test(x)) {
        doTo.accept(x);     
    }
}

Test it on a list of 10 Foo objects:

public static void main(String[] args) {
    List<Foo> foos = IntStream.range(0, 10)
         .mapToObj(i -> new Foo())
         .collect(Collectors.toList());

    for (Foo foo : foos) {
        doToIf((Foo x) -> x.getBar() % 2 == 0, Foo::println, foo);
    }
}

=> 6 2 0 0 4

Which can be shortened to:

public static void main(String[] args) {
    IntStream.range(0, 10)
       .mapToObj(i -> new Foo())
       .filter((Foo x) -> x.getBar() % 2 == 0)
       .forEach(Foo::println);
}