Interface Function được sử dụng để chuyển đổi dữ liệu từ tham số truyền vào để trả về kết quả ở định dạng khác. Để làm được điều này, chúng ta sẽ implement phương thức abstract apply() được định nghĩa trong interface Function.
Nhưng trước tiên hãy xem nội dung của interface Function xem thế nào nhé các bạn!
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; } } |
Chúng ta thường sử dụng interface Function trong phương thức map() của đối tượng Stream với vai trò là tham số của phương thức này, ví dụ như sau:
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); } } |
Như các bạn thấy, interface Function được định nghĩa với 2 kiểu data generic, đầu tiên sẽ là input được truyền vào parameter của phương thức apply() và cái thứ hai sẽ là output của phương thức apply() này. Trong ví dụ của mình, dữ liệu đầu vào là kiểu dữ liệu String, và sau khi chuyển đổi, dữ liệu trả về là kiểu Integer.
Chúng ta có thể đưa định nghĩa của Function f vào phương thức map như sau:
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); } } |
Kết quả:
Phương thức compose() dùng để chuyển đổi nhiều bước khác nhau, kết quả của function sau sẽ là input của function trước. Ví dụ:
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); } } |
Trong đoạn code trên các bạn sẽ thấy Function f2 sẽ được thực thi trước, sau đó mới tới Function f1.
Kết quả:
Phương thức andThen() cũng tương tự như phương thức compose(), cũng dùng để chuyển đổi nhiều bước khác nhau nhưng nó xử lý theo thứ tự, kết quả của function trước sẽ là input của function sau. Ví dụ như sau khi chuyển đổi String sang Integer thì chúng ta chuyển đổi tiếp các giá trị Integer này sang String với giá trị tăng lên 1 như sau:
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); } } |
Kết quả:
Phương thức static identity() được dùng để trả về giá trị của input. Bạn truyền vào giá trị gì thì output của phương thức này là giá trị đó. Ví dụ:
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); } } |
Kết quả:
Chúng ta có nhiều biến thể của Interface Function tuỳ thuộc vào kiểu dữ liệu trả về. Ví dụ như nếu trả về kiểu Integer chúng ta có interface ToIntFunction hay trả về kiểu Long chúng ta có interface ToLongFunction, …
BiFunction interface là một biến thể của Function interface cho phép chúng ta có thể truyền 2 giá trị cho input và trả về giá trị output, ví dụ như:
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); } } |
Ở đây, mình định nghĩa một BiFunction với 2 input kiểu giá trị String, output kiểu giá trị Integer để return lại độ dài của một chuỗi sau khi nối 2 chuỗi được truyền vào ở input.
Kết quả: