Java Lambda Functions: Function, Consumer, Supplier, Predicate
Let’s look at Lambda, which was introduced in Java 8.
What Is a Lambda Function?
Lambda function is a term used in programming languages to refer to anonymous functions. A lambda can be used temporarily and immediately, so it can be treated as a disposable function.
Basic Syntax
(argument) -> {processing}
()-> {processing}
(argument) -> {processing}
(argument) -> {return 0;}
You can use Lambda easily by connecting arguments and processing with ->. Let’s take a closer look through several examples below.
Basic Form
In Java 7 and earlier, thread code was usually written as follows.
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world lambda");
}
}).start();
When using a lambda, the code becomes shorter and much easier to read, as shown below.
new Thread(()->{
System.out.println("Hello world lambda");
}).start();
Lambda with List
A normal for statement can be replaced with a lambda passed to the forEach function.
Suppose there is a list like the following.
List<String> stringList = new ArrayList<String>();
stringList.add("apple");
stringList.add("orange");
stringList.add("strawberry");
In Java 7 and earlier, you would have used a for statement like this.
for (String string : stringList) {
System.out.println(string);
}
From Java 8 onward, you can write it as follows by using Lambda.
stringList.forEach(string -> System.out.println(string));
Because Lambda allows arguments and processing syntax to be omitted, you can write it in the shortest reasonable form.
The omission rules are as follows.
- If the return value is
void,returncan be omitted. - If the processing code is one line, braces
{ }are not required, and the final semicolon;can also be omitted. - If there is one argument, parentheses
()can be omitted. - The data type of arguments can be omitted. There are two styles: omit all types or write all types.
There are other omission rules, but they will not all be covered here.
You can also write it using a method reference as follows.
stringList.forEach(System.out::println);
When method references are used together with Lambda, the code can be concise and easy to understand. However, if they are overused, the code can become harder to read, so it is better to use them only for explicit processing such as Objects::nonNull or XXXX::getId.
Lambda with Arrays
You may want to write the same kind of code for arrays, but arrays cannot use forEach directly, so they must be converted to a List first.
String[] stringArray = { "apple", "orange", "strawberry" };
Arrays.asList(stringArray).forEach(string -> System.out.println(string));
- When using Lambda with arrays, you can also use the
Arrays.stream()method.
Lambda with Map
With Map, you can apply Lambda directly.
Suppose there is a map like the following.
Map<String, String> stringMap = new HashMap<String, String>();
stringMap.put("apple", "red");
stringMap.put("orange", "orange");
stringMap.put("strawberry", "red");
In Java 7 and earlier, you would have used a for statement like this.
for (Entry<String, String> entry : stringMap.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
From Java 8 onward, you can write it as follows by using Lambda.
stringMap.forEach((key, value) -> {
System.out.println(key);
System.out.println(value);
});
Functional Interface
A functional interface is an interface that has exactly one abstract method. In other words, it is an annotation that lets functions be handled like first-class objects, and it restricts an interface so that it has only one abstract method.
First, create a functional interface.
@FunctionalInterface
public interface Math {
int calc(int first, int second);
}
Now implement and run the interface method.
public class MathCalc {
public static void main(String[] args) {
Math plus = (first, second) -> first + second;
System.out.println(plus.calc(3, 2));
Math minus = (first, second) -> first - second;
System.out.println(plus.calc(3, 2));
}
}
Result:
5
5
Four Functional Interfaces
Java already provides commonly used functional interfaces. There are four main functional interfaces.
| Functional interface | Parameter | Return value |
|---|---|---|
Supplier<T> |
X | O |
Consumer<T> |
O | X |
Function<T, R> |
O | O |
Predicate<T> |
O | Boolean |
Supplier <T>
- Supplier means “provider” in English, and it is a functional interface that has a return value without parameters.
- It has
T get()as its abstract method.
Supplier functional interface
package java.util.function;
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Usage example
Supplier<String> supplier = () -> "Hello World!";
System.out.println(supplier.get());
Result:
Hello World!
Consumer<T>
- Consumer means “consumer” in English. It is a functional interface that receives an object
Tas a parameter and uses it, but has no return value. - It has
void accept(T t)as its abstract method. - It also provides a function called
andThen, which allows the nextConsumerto be chained after one function finishes.
Consumer functional interface
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Usage example
Consumer<String> consumer = (str) -> System.out.println(str.split(" ")[0]);
consumer.andThen(System.out::println).accept("Hello World!");
Result:
Hello
Hello World!
In the example above, the Consumer received by accept is processed first and displays “Hello”. Then the second Consumer received by andThen is processed and displays “Hello World!”. In functional programming, functions do not assign or change values, so even though the first Consumer changed the data with split, the original data is preserved.
Function<T, R>
- Function means “function” in English. It is a functional interface that receives an object
Tas a parameter, processes it, and returns it asR. - It has
R apply(T t)as its abstract method. - Like
Consumer,FunctionprovidesandThen, and it additionally providescompose. - If
andThenconnects the next function to run after the first function executes,composediffers in that it connects a function to run before the first function executes. - There is also an
identityfunction, which is a static function that returns itself.
Function functional interface
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
Usage example
Function<String, Integer> function = str -> str.length();
int length = function.apply("Hello World!");
System.out.println(length);
Result:
12
Predicate<T>
- Predicate means “to assert as true”. It is a functional interface that receives an object
Tas a parameter, processes it, and returns a Boolean. - It has
boolean test(T t)as its abstract method.
Predicate functional interface
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
@SuppressWarnings("unchecked")
static <T> Predicate<T> not(Predicate<? super T> target) {
Objects.requireNonNull(target);
return (Predicate<T>)target.negate();
}
}
Usage example
Predicate<String> predicate = (str) -> str.equals("Hello World!");
boolean test = predicate.test("Hello World!");
System.out.println(test);
Result:
true