The functional interfaces provided by Jdk8 are all under the java.util.function package. The functional interfaces of Jdk8 are marked with the @FunctionInterface annotation, but in fact, even if there is no interface marked with this annotation, there is only one abstract method. It can be regarded as a functional interface.
The four core functional interfaces built into JDK8 are as follows:
functional interface | Interface Type | Parameter Type | return type | effect | Application scenarios in Stream |
---|---|---|---|---|---|
Consumer<T> | consumer interface | T | void | Operates on an object of type T, containing the method accpet(T t) | The functional interfaces of methods such as forEach and peek are of type Consumer |
Supplier<T> | supply interface | none | T | Returns an object of type T with method T get() | Some method overloads of methods such as collect use the Supplier type |
Function<T,R> | functional interface | T | R | Operates on an object of type T and returns an object of type R, including the method R apply(T t) | The functional interfaces of methods such as map and flatMap are of type Function |
Predicate<T> | Assertive interface | T | boolean | Determines whether the object of type T satisfies the constraint, and returns the constraint result, including the method boolean test(T t) | The functional interfaces of methods such as filter are of type Predicate |
Consumer<T>
Consumer<T> consumer interface, as the name implies, consumes and processes parameters without feeding back the calling environment
basic use
public class Main { /** * Consumer<T> * Consumption interface: As the name suggests, it is mainly used to consume parameters, and does not feed back the calling environment (no return value) * accept: Abstract method implementation, used to invoke the method. * andThen: The default implementation method, which internally allows us to chain calls */ public static void main(String[] args) { // The given string is converted to uppercase and output to the console, implemented as an anonymous inner class Consumer<String> con1 = new Consumer<String>() { @Override public void accept(String str) { System.out.println("By way of anonymous inner class:"+ str.toUpperCase()); } }; // When executing this method, we pass in the given parameter string, and it will execute the above implementation. accept method and pass in parameters, and finally execute our given code logic con1.accept("abc"); // Convert the given string to uppercase and output to the console via Lambda expression implementation Consumer<String> con2 = (text)-> System.out.println("pass Lambda way of expression:"+ text.toUpperCase()); con2.accept("goods"); /** * Final result: * By way of anonymous inner class: ABC * By way of Lambda expressions: GOOD * With lambda expressions, we only need to remember the parameter list and execution logic, and we don't need to care about the rest. */ }}
learning case
public class Main { /** * Consumer<T> * Consumption interface: As the name suggests, it is mainly used to consume parameters, and does not feed back the calling environment (no return value) * accept: Abstract method implementation, used to invoke the method. * andThen: The default implementation method, which internally allows us to chain calls */ public static void main(String[] args) { // 1. We need to sort the collection and output it to the console Consumer<List> con1 = list-> { System.out.println("Collection before sorting:"+ list); Collections.sort(list); System.out.println("Sorted collection:"+list); }; con1.accept(Arrays.asList(1,5,3,2,9,6,7)); /** * Final result: * Set before sorting: [1, 5, 3, 2, 9, 6, 7] * Sorted set: [1, 2, 3, 5, 6, 7, 9] */ // The above execution logic is implemented in two steps. The first step is to obtain the given set for sorting, and the second is to output the sorted set. // If the above two steps can be implemented with two consumers respectively, we can define a method to receive two consumers for operation accept(Arrays.asList(1,5,3,2,9,6,7),list-> { System.out.println("andThen Chained call-before-collection:"+list); Collections.sort(list); },list-> System.out.println("andThen Chained call back collection:"+ list)); /** * Final result: * andThen The set before the chain call: [1, 5, 3, 2, 9, 6, 7] * andThen Set after chained call: [1, 2, 3, 5, 6, 7, 9] */ // If there are multiple consumer parameters, we can directly make chain calls in the Lambda expression, and we don't need to work hard to define the method. Consumer<List> con2 = ((Consumer<List>) list -> { System.out.println("lambda A pre-chained collection of expressions:" + list); Collections.sort(list); }).andThen(list -> System.out.println("lambda Chained collection of expressions:"+list)); // It should be noted that: to use this method, the first consumer must be forcibly specified as the (Consumer) type to make a chain call, and the subsequent interfaces can call the method con2.accept(Arrays.asList(1,53,31,25,99,62,17)); /** * Final result: * lambda The pre-chained set of expressions: [1, 53, 31, 25, 99, 62, 17] * lambda Chained set of expressions: [1, 17, 25, 31, 53, 62, 99] */ } public static void accept(List<Integer> list,Consumer<List> con1,Consumer<List> con2){ // When the chain is called, the interface implementation on the left will be executed first, and then executed sequentially to the right. Our requirement is to sort first and then output. The first Consumer is sorting, and the second is output. con1.andThen(con2).accept(list); } }
Summarize
1. The essence of functional interface is actually to pass functions as parameters
2.Consumer is a consumer-type functional interface, usually used for internal data processing, and has no return value
3. In addition to Consumer, there are various consumer functional interfaces, IntConsumer, LongConsumer, etc. If you need to pass two parameters, you can use BIFunction, or you can customize it according to your own needs.
Supplier<T>
Consumer<T> supply-type functional interface, as the name implies, provides data to the calling environment and does not receive parameters to pass
basic use
public class Main{ /** * The supply-type functional interface, as the name implies, is to supply data to the calling environment without receiving parameters to pass. * T get() : Returns a parameter of type generic T to the calling environment */ public static void main(String[] args) { // returns a 0-100 random number between Supplier<Integer> sup1 = new Supplier<Integer>() { @Override public Integer get() { int res = new Random().nextInt(100); System.out.println("The random number obtained by the anonymous inner class:"+ res); return res; } }; // When the method is executed, it will execute the above implementation get method. sup1.get(); // pass lambda Implemented in the form of expressions Supplier<Integer> sup2 = ()-> { int res = new Random().nextInt(100); System.out.println("pass lambda The random number obtained by the way of expression:"+ res); return res;}; sup2.get(); /** * Final result: * Random number obtained by anonymous inner class: 28 * Random number obtained by lambda expression: 62 * With lambda expressions, we only need to remember the parameter list and execution logic, and we don't need to care about the rest. */ } }
Study case
public class Main{ public static Map<String,String> redis = new HashMap(); /** * The supply-type functional interface, as the name implies, is to supply data to the calling environment without receiving parameters to pass. * T get() : Returns a parameter of type generic T to the calling environment */ public static void main(String[] args) { // 1.(simulation) to query a Key exist redis Whether there is a cache, if there is no cache, it will be fetched from the database and stored redis Return again, if there is any, return directly String val = getCache("title"); String val2 = getCache("title"); String val3 = getCache("title"); /** * Final result: Fetch from database: I am the title Fetch from cache: I am the title Fetch from cache: I am the title */ // It can be seen that after the first follow-up, the data is taken directly from the cache } public static String getCache(String key){ String val = redis.get(key); if(Objects.isNull(val)){ // Get data from database val = getDbVal(() -> "I am the title"); System.out.println("get from database:"+val); redis.put(key,val); return val; } System.out.println("get from cache:"+val); return val; } public static String getDbVal(Supplier<String> supplier){ return supplier.get(); } }
Summarize
1. The essence of functional interface is actually to pass functions as parameters
2.Supplier is a supply-type functional interface, which is usually used to construct an object and return to the calling environment after processing.
3. In addition to Supplier, there are various supply-type functional interfaces, as well as BooleanSupplier, IntSupplier, etc.
Function<T,R>
Function<T,R> is the functional interface of the functional type. The generic type T is the parameter, the generic type R is the return value, and the main application scenarios are data type conversion.
basic use
public class Main{ /** * Function<T,R>The functional interface of the function type, the generic type T is the parameter, the generic type R is the return value, and the main application scenarios are data type conversion. * R apply(T t): Abstract method implementation for calling a method and returning a generic R * <V> Function<T, V> andThen: The default implementation method, which allows us to chain calls internally, is consistent with other andThen principles. * <V> Function<V, R> compose: The default implementation method allows us to chain calls internally. The calling method is the same as andThen, but the execution order is different. Compose first executes the function interface in compose, and then executes the function interface called on the left, and then turns to the left. * <T> Function<T, T> identity(): Returns the currently executed method, from the source code we can also see that it returns the current t */ public static void main(String[] args) { // Pass in the given string and return the converted Integer type Function<String,Integer> fun1 = new Function<String, Integer>() { @Override public Integer apply(String s) { Integer convert = Integer.valueOf(s); System.out.println("The value obtained by the anonymous inner class:"+ convert +",Is the data type Integer?result:" + (convert instanceof Integer)); return convert; } }; // When this method is executed, it will execute the apply method we implemented above. fun1.apply("10086"); // Implemented by means of lambda expressions Function<String,Integer> fun2 = s->{ Integer convert = Integer.valueOf(s); System.out.println("pass lambda The value obtained by the way of expression:"+ convert +",Is the data type Integer?result:" + (convert instanceof Integer)); return convert; }; fun2.apply("10000"); /** * The value obtained by the anonymous inner class: 10086, is the data type Integer? result: true * The value obtained by lambda expression: 10000, is the data type Integer? result: true * With lambda expressions, we only need to remember the parameter list and execution logic, and we don't need to care about the rest. */ // We continue to understand and supplement the API of Function, after all, this thing is often used in work // andThen We all know that it is often used in chained calls. Here we must ensure that T and V are of the same type, that is, the parameter generic T and the return value generic V Function<String,String> fun3 = x-> { System.out.println("I'm fun3 Methods"); return x; }; Function<String,String> fun4 = y-> { System.out.println("I'm fun4 Methods"); return y; }; fun3.andThen(fun4).apply("test"); /** * Final result: * i'm fun3's method * i'm fun4's method */ // We found that the apply method of fun3 is executed first and then the apply method of fun4 is executed. // compose is a chain call like andThen, but the results are very different. Here, it must be ensured that T and V types are the same, that is, the parameter generic T and the return value generic V fun3.compose(fun4).apply("test"); /** * Final result: * i'm fun4's method * i'm fun3's method */ // We found that the apply method of fun4 is executed first and then the apply method of fun3 is executed. // From this, we infer that the difference between compose and andThen is that the execution order of compose interface methods is from right to left, while andThen is from left to right. Function<Object, Object> identity = Function.identity(); // The Function.identity() static method is not easy to demonstrate here. This is usually used later when it is used to transfer the Map type with Stream. It returns itself } }
learning case
public class Main{ /** * Function<T,R>The functional interface of the function type, the generic type T is the parameter, the generic type R is the return value, and the main application scenarios are data type conversion. * R apply(T t): Abstract method implementation for calling a method and returning a generic R * <V> Function<T, V> andThen: The default implementation method, which allows us to chain calls internally, is consistent with other andThen principles. * <V> Function<V, R> compose: The default implementation method allows us to chain calls internally. The calling method is the same as andThen, but the execution order is different. Compose first executes the function interface in compose, and then executes the function interface called on the left, and then turns to the left. * <T> Function<T, T> identity(): Returns the currently executed method, from the source code we can also see that it returns the current t */ public static void main(String[] args) { List<Person> persons = Arrays.asList(new Person(1,"Zhang San"),new Person(2,"Li Si")); // Given a set of person objects, convert it to a set of name attributes and return Function<List<Person>,List<String>> fun1 = list-> { List<String> arr = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { arr.add(list.get(i).getName()); } return arr; }; List<String> personNames = fun1.apply(persons); System.out.println(personNames); /** * Final result: * Result: [Zhang San, Li Si] */ } } class Person{ private Integer id; private String name; public Person(Integer id, String name) { this.id = id; this.name = name; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public Integer getId() { return id; } public String getName() { return name; } }
Summarize
1. The essence of functional interface is actually to pass functions as parameters
2.Function is a functional functional interface, which is usually used to construct an object and return to the calling environment after processing.
3. In addition to Function, there are various functional interfaces, such as BIFunction, ToIntFunction, etc.
Predicate
Predicate<T> is a functional interface of predicate type, and generic type T is a functional interface of parameter and return result type of Boolean type.
basic use
public class Main{ /** * Predicate<T>Assertion type functional interface, generic type T is a functional interface with parameters and return result type of Boolean type. * boolean test(T t): Abstract method implementation, used to return the Boolean type result after the logical operation of the incoming parameters * Predicate<T> and: The default implementation method, which allows us to chain calls internally, is used to determine the implementation of multiple Predicate function interfaces at the same time, similar to the short-circuit & operation in logical operations. * Predicate<T> negate: The default implementation method, which allows us to chain calls internally, is used to determine the implementation of multiple Predicate function interfaces at the same time, and is used to invert the current judgment result and return it, similar to the ! operation in logical operations * Predicate<T> or: The default implementation method, which allows us to chain calls internally, is used to determine the implementation of multiple Predicate function interfaces at the same time, similar to the || operation in logical operations. * Predicate<T> isEqual: The static method allows us to chain calls internally. In the case of ensuring that the parameters are not empty, its internal implementation logic actually calls the equals of Object. The specific equals depends on whether the subclass has rewritten it. */ public static void main(String[] args) { Predicate<String> pre1 = new Predicate<String>() { @Override public boolean test(String o) { boolean bool = o.matches("[0-9]{1,}"); System.out.println("The value obtained by the anonymous inner class:"+ bool); return bool; } }; // When the method is executed, it will execute the above implementation test method. pre1.test("10086"); Predicate<String> pre2 = text->{ boolean bool = text.matches("[0-9]{1,}"); System.out.println("pass lambda The value obtained by the way of expression:"+ bool); return bool; }; pre2.test("10086a"); /** * Final result: * The value obtained by the anonymous inner class: true * The value obtained by lambda expression: false * With lambda expressions, we only need to remember the parameter list and execution logic, and we don't need to care about the rest. */ // we continue to Predicate of API Do some understanding and supplements, after all, this stuff is often used at work // and Effectively equivalent to short-circuiting in logical operators&operate Predicate<String> fun3 = x-> { System.out.println("Calculate first fun3"); return true; }; Predicate<String> fun4 = x-> { System.out.println("Calculate first fun4"); return false; }; System.out.println("the first time and result:"+fun3.and(fun4).test("test")); /** * Final result: * Calculate fun3 first * Calculate fun4 first * This result: false */ // So why do we know it's a short circuit&operation instead of&operation? , we just need to return the first functional interface false,See if it will execute the second functional interface Predicate<String> fun5 = x-> { System.out.println("Calculate first fun5"); return false; }; Predicate<String> fun6 = x-> { System.out.println("Calculate first fun6"); return true; }; System.out.println("the second time and result:"+fun5.and(fun6).test("test")); /** * Final result: * Calculate fun5 first * Second time and result: false. */ // From the results we can actually infer that the first result is true in the case of the second fun6 It didn't go in at all, so it's a short circuit& // and starts at and We can also see in the method source code return (t) -> test(t) && other.test(t); is a short circuit& // negate is actually equivalent to the logical operator in!operate // We directly take the above value as an example, the original result should be false,After negation, it should be true System.out.println("negate result:"+fun5.and(fun6).negate().test("test")); /** * Final result: * negate result: true */ // or is equivalent to the logical operator in||operate // Let's take the above example directly, the first one is false,The second is true,||The final result should be true System.out.println("or result:"+fun5.or(fun6).test("test")); /** * Final result: * or result: true */ // isEqual The internal call is Object of equals method, if the subclass overrides it equals then call the subclass equals method // as we use String rewrite Object of equals method, we use it as an example Predicate<String> fun7 = Predicate.isEqual("Hello"); System.out.println("isEquals First result:"+ fun7.test("Hello")); /** * Final result: * isEquals first result: true */ Predicate<String> fun8 = Predicate.isEqual("World"); System.out.println("isEquals Second result:"+ fun8.test("Hello")); // Custom object types are also comparable, but need to be rewritten equals and hashCode,I won't write an example here, you can play by yourself // The above is Predicate related to API introduction } }
learning case
public class Main{ /** * Predicate<T>Assertion type functional interface, generic type T is a functional interface with parameters and return result type of Boolean type. * boolean test(T t): Abstract method implementation, used to return the Boolean type result after the logical operation of the incoming parameters * Predicate<T> and: The default implementation method, which allows us to chain calls internally, is used to determine the implementation of multiple Predicate function interfaces at the same time, similar to the short-circuit & operation in logical operations. * Predicate<T> negate: The default implementation method, which allows us to chain calls internally, is used to determine the implementation of multiple Predicate function interfaces at the same time, and is used to invert the current judgment result and return it, similar to the ! operation in logical operations * Predicate<T> or: The default implementation method, which allows us to chain calls internally, is used to determine the implementation of multiple Predicate function interfaces at the same time, similar to the || operation in logical operations. * Predicate<T> isEqual: The static method allows us to chain calls internally. In the case of ensuring that the parameters are not empty, its internal implementation logic actually calls the equals of Object. The specific equals depends on whether the subclass has rewritten it. */ public static void main(String[] args) { // To determine whether the given string is a pure number and less than 10, you can use and to make chain calls Predicate<String> pre1 = ((Predicate<String>) s -> s.matches("[0-9]{1,}")).and(x->Integer.valueOf(x) <10); System.out.println("use and way to chain calls:"+pre1.test("9")); // It should be noted that: to use this method, the first Predicate must be forcibly re-specified as the (Predicate) type in order to make a chain call, so that subsequent interfaces can call the method // In fact, this method is rarely used, because it is more troublesome, so generally use && directly for judgment Predicate<String> pre2 = s-> s.matches("[0-9]{1,}") && Integer.valueOf(s) <10; System.out.println("use&&way to call:"+pre2.test("10")); /** * Final result: * Use the and method to chain calls: true * Use && method to call: false */ } }
The above is the basic usage and some simple cases of the four basic functions provided by Jdk8 (in addition to these four major categories, there are many functional interfaces, and you can also customize the functional interfaces to meet our needs) and some simple cases. How to do it? How to write it needs to be done according to the actual needs of the project. Usually, functional interfaces will be used in sets with Stream. At present, there are many frameworks that support functional interfaces, such as MyBatis-plus and other frameworks with high community activity.