Sometimes when writing software, it is useful to pass a function as an argument to another function. A common example for this is a generic filter function. In pseudo code, it would look something like this:

1
2
3
4
5
6
7
8
9
10
filter(persons, filter_function) {
  filtered = [];
  for (person in persons) {
    if (filter_function(person)) {
      filtered.push(person);
    }
  }

  return filtered;
}

In the example above, we provide a list of persons and a filter_function. The filter_function gets applied on each person and if it returns false, the person is discarded.

Interfaces

We can implement something similar to the example above using interfaces. We define an interface with a single method and pass an object implementing this interface to a function. Let’s see how it looks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.util.ArrayList;

public class Interface {
  // Interface of the filter
  private interface Filter<T> {
    public boolean filter(T t);
  }

  // Function that filters numbers based on a given filterFunction
  public static ArrayList<Integer> filter(int[] numbers, Filter<Integer> filterFunction) {
    ArrayList<Integer> filtered = new ArrayList<Integer>();
    for (int i = 0; i < numbers.length; i++) {
      if (filterFunction.filter(numbers[i])) {
        filtered.add(numbers[i]);
      }
    }

    return filtered;
  }

  public static void main(String[] args) {
    // Implement the interface
    Filter<Integer> customFilter = new Filter<Integer>() {
      public boolean filter(Integer number) {
        return number < 10;
      }
    };

    int[] numbers = {1, 4, 11};

    // Use our custom filter
    ArrayList<Integer> result = filter(numbers, customFilter);

    // Print result
    for (int i = 0; i < result.size(); i++) {
      System.out.println(result.get(i));
    }
  }
}

Lambdas

A lambda is just a shorter way to define our single method interface. The example would look like this using a lambda:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.util.ArrayList;

public class Interface {
  private interface Filter<T> {
    public boolean filter(T t);
  }

  public static ArrayList<Integer> filter(int[] numbers, Filter<Integer> filterFunction) {
    ArrayList<Integer> filtered = new ArrayList<Integer>();
    for (int i = 0; i < numbers.length; i++) {
      if (filterFunction.filter(numbers[i])) {
        filtered.add(numbers[i]);
      }
    }

    return filtered;
  }

  public static void main(String[] args) {
    // This is the only part that changed
    Filter<Integer> customFilter = (Integer number) -> {
      return number < 10;
    };

    int[] numbers = {1, 4, 11};

    ArrayList<Integer> result = filter(numbers, customFilter);

    for (int i = 0; i < result.size(); i++) {
      System.out.println(result.get(i));
    }
  }
}

The compiler can infer the types of the arguments, so we can further simplify the lambda:

1
2
3
Filter<Integer> customFilter = (number) -> {
  return number < 10;
};

For single statement lambdas we can omit the braces and the return keyword:

1
Filter<Integer> customFilter = (number) -> number < 10;

For single argument lambdas we can also omit the parentheses:

1
Filter<Integer> customFilter = number -> number < 10;

Conclusion

This article shows the syntax of lambda expressions and how they can be used to pass a function as an argument.

[ design_patterns  java  programming  ]
Monitoring Kubernetes Resources with Fabric8 Informers
Fabric8 Kubernetes Java Client
Kubernetes Java Client
Dependency injection (Inversion of Control) in Spring framework
Flyway - Version control for Databases in Java