Function interface is used to convert data from input parameters to return results in a different format. To do this, we will implement the apply() abstract method defined in the Function interface.
But first, let’s look at the contents of the Function interface.
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
package java.util.function; import java.util.Objects; /** * Represents a function that accepts one argument and produces a result. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #apply(Object)}. * * @param <T> the type of the input to the function * @param <R> the type of the result of the function * * @since 1.8 */ @FunctionalInterface public interface Function<T, R> { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t); /** * Returns a composed function that first applies the {@code before} * function to its input, and then applies this function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of input to the {@code before} function, and to the * composed function * @param before the function to apply before this function is applied * @return a composed function that first applies the {@code before} * function and then applies this function * @throws NullPointerException if before is null * * @see #andThen(Function) */ default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } /** * Returns a composed function that first applies this function to * its input, and then applies the {@code after} function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of output of the {@code after} function, and of the * composed function * @param after the function to apply after this function is applied * @return a composed function that first applies this function and then * applies the {@code after} function * @throws NullPointerException if after is null * * @see #compose(Function) */ default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } /** * Returns a function that always returns its input argument. * * @param <T> the type of the input and output objects to the function * @return a function that always returns its input argument */ static <T> Function<T, T> identity() { return t -> t; } } |
We often use the Function interface in the map() method of the Stream object as the parameter of this method, for example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.huongdanjava.javaexample; import java.util.Arrays; import java.util.List; import java.util.function.Function; public class Example { public static void main(String[] args) { Function<String, Integer> f = (s) -> s.length(); List<String> names = Arrays.asList("Khanh", "Quan", "Khang"); names.stream() .map(f) .forEach(System.out::println); } } |
As you can see, the Function interface is defined with two generic data types, the first will be the input passed to the parameter of the apply() method and the second will be the output of this apply() method. In my example, the input data is a String data type, and after conversion, the returned data is of type Integer.
We can pass the definition of Function f to the map method as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.huongdanjava.javaexample; import java.util.Arrays; import java.util.List; public class Example { public static void main(String[] args) { List<String> names = Arrays.asList("Khanh", "Quan", "Khang"); names.stream() .map(s -> s.length()) .forEach(System.out::println); } } |
Result:
The compose() method is used to convert many different steps, the result of the next function will be the input of this function. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.huongdanjava.javaexample; import java.util.Arrays; import java.util.List; import java.util.function.Function; public class Example { public static void main(String[] args) { Function<String, String> f1 = s -> s.substring(0, 2); Function<String, String> f2 = s -> s.toUpperCase(); List<String> names = Arrays.asList("Khanh", "Quan", "Khang"); names.stream() .map(f1.compose(f2)) .forEach(System.out::println); } } |
In the above code, you will see Function f2 will be executed first, then Function f1.
Result:
The andThen() method same as compose() method, used to convert with many different steps too, but sequentially, the result of this function will be the input of the next function. For example, after converting a String to Integer, we pass this Integer value to String with a value of 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.huongdanjava.javaexample; import java.util.Arrays; import java.util.List; import java.util.function.Function; public class Example { public static void main(String[] args) { List<String> names = Arrays.asList("Khanh", "Quan", "Khang"); Function<String, Integer> f1 = s -> s.length(); Function<Integer, String > f2 = i -> String.valueOf(i + 1); names.stream() .map(f1.andThen(f2)) .forEach(System.out::println); } } |
Result:
The static identity() method is used to return the value of the input. Whatever value you pass in, the output of this method will be that value. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdanjava.javaexample; import java.util.Arrays; import java.util.List; import java.util.function.Function; public class Example { public static void main(String[] args) { List<String> names = Arrays.asList("Khanh", "Quan", "Khang"); names.stream() .map(Function.identity()) .forEach(System.out::println); } } |
Result:
We have many variants of the Function interface depending on the type of data returned. For example, if we return an Integer we have a ToIntFunction interface or we return a Long, we have the ToLongFunction interface.
BiFunction interface is a variant of Function interface that allows us to pass 2 values to input and return output value, for example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.huongdanjava.javaexample; import java.util.function.BiFunction; public class Example { public static void main(String[] args) { BiFunction<String, String, Integer> f = (s1, s2) -> s1.concat(s2).length(); Integer length = f.apply("Hello", "Khanh"); System.out.println(length); } } |
Here, I define a BiFunction with 2 input String values, output type Integer to return the length of a string after concatenating 2 strings passed in the input.
Result: