Functional interface
concept
Functional interface in Java refers to an interface with only one abstract method
Functional interface, that is, the interface suitable for functional programming scenarios. The embodiment of functional programming in Java is Lambda, so functional interface is the interface that can be used by Lambda. Only by ensuring that there is and only one abstract method in the interface can Lambda in Java be deduced smoothly.
format
Just ensure that there is and only one abstract method in the interface:
Modifier interface Interface name { public abstract Return value type method name(Optional parameter information); // Other non abstract method content }
Since the public abstract of the abstract method in the interface can be omitted, it is very simple to define a functional interface:
public interface MyFunctionalInterface { void myMethod(); }
@Functional interface annotation
Similar to the @ Override annotation, Java 8 specifically introduces a new annotation for functional interfaces: @ functional interface. This annotation can be used for the definition of an interface:
@FunctionalInterface public interface MyFunctionalInterface { void myMethod(); }
Custom functional interface
For the newly defined MyFunctionalInterface functional interface, the typical usage scenario is as a method parameter:
public class Demo01 { // Use a custom functional interface as a method parameter private static void doSomething(MyFunctionalInterface inter) { inter.myMethod(); // Call a custom functional interface method } public static void main(String[] args) { // Call a method that uses a functional interface doSomething(()-> System.out.println("Lambda Execute!")); } } @FunctionalInterface interface MyFunctionalInterface { void myMethod(); }
Functional programming
Delayed execution of Lambda
After the code of some scenarios is executed, the results may not be used, resulting in performance waste. Lambda expressions are delayed, which can be used as a solution to improve performance.
Log cases of performance waste:
Note: logs can help us quickly locate problems and record the situation during the operation of the program, so as to facilitate the monitoring and optimization of the project. A typical scenario is to conditionally use parameters. For example, after splicing log messages, print them when the conditions are met:
public class Dome02 { public static void log(int level, String msg) { if (level == 1) { System.out.println(msg); } } public static void main(String[] args) { String msgA = "Hello"; String msgB = "World"; String msgC = "Java"; log(1, msgA + msgB + msgC); } }
There is a problem with this Code: whether the level meets the requirements or not, as the second parameter of the log method, the three strings must be spliced and passed into the method first, and then the level judgment will be made. If the level does not meet the requirements, the string splicing operation will be done in vain, resulting in performance waste.
Experience Lambda's better writing:
public class Demo03 { private static void log(int level, MessageBuilder builder) { if (level == 1) { System.out.println(builder.buildMessage()); } } public static void main(String[] args) { String msgA = "Hello"; String msgB = "World"; String msgC = "Java"; log(1, () -> msgA + msgB + msgC ); } } @FunctionalInterface interface MessageBuilder { String buildMessage(); }
In this way, only when the level meets the requirements will the three strings be spliced; Otherwise, the three strings will not be spliced.
Demonstrate Lambda's delay:
public class Demo04 { private static void log(int level, MessageBuilder builder) { if (level == 1) { System.out.println(builder.buildMessage()); } } public static void main(String[] args) { String msgA = "Hello"; String msgB = "World"; String msgC = "Java"; log(2, () -> { System.out.println("Lambda Execute!"); return msgA + msgB + msgC; }); } }
It can be seen from the results that Lambda will not execute if the level requirements are not met. So as to achieve the effect of saving performance.
Use Lambda as the parameter and return value
Regardless of the implementation principle, Lambda expressions in Java can be regarded as a substitute for anonymous inner classes. If the parameter of a method is a functional interface type, you can use a Lambda expression instead. Using a Lambda expression as a method parameter is actually using a functional interface as a method parameter.
For example, Java The lang. Runnable interface is a functional interface. Assuming that a startThread method uses this interface as a parameter, you can use Lambda to pass parameters. In fact, this situation is not fundamentally different from the construction method parameter of Thread class, which is Runnable.
public class Demo05 { private static void startThread(Runnable task) { new Thread(task).start(); } public static void main(String[] args) { startThread(() -> System.out.println("Thread task execution!")); } }
Similarly, if the return value type of a method is a functional interface, you can directly return a Lambda expression. When you need a method to get a Java util. When an object of comparator interface type is used as a sorter, you can call this method to obtain
import java.util.Arrays; import java.util.Comparator; public class Demo06{ private static Comparator<String> newComparator() { return (a,b) -> b.length() - a.length(); } public static void main(String[] args) { String[] array = { "abc", "ab", "abcd" }; System.out.println(Arrays.toString(array)); Arrays.sort(array, newComparator()); System.out.println(Arrays.toString(array)); } }
Directly return a Lambda expression.
Common functional interfaces
JDK provides a large number of commonly used functional interfaces to enrich the typical usage scenarios of Lambda, mainly in Java util. Function package. The following are the simplest interfaces and use examples.
Supplier interface
java. util. function. The supplier interface contains only one parameterless method: T get(). Used to get object data of the type specified by a generic parameter. Because this is a functional interface, it means that the corresponding Lambda expression needs to "provide" an object data conforming to the generic type.
import java.util.function.Supplier; public class Demo07 { private static String getString(Supplier<String> function) { return function.get(); } public static void main(String[] args) { String msgA = "Hello"; String msgB = "World"; System.out.println(getString(() -> msgA + msgB)); } }
Consumer interface
java. util. function. The consumer interface is just opposite to the Supplier interface. Instead of producing a data, it consumes a data, and its data type is determined by generics.
Abstract method: accept
The Consumer interface contains the abstract method void accept (T), which means to consume data of a specified generic type. Basic usage, such as:
import java.util.function.Consumer; public class Demo08 { private static void consumeString(Consumer<String> function) { function.accept("Hello"); } public static void main(String[] args) { consumeString(s -> System.out.println(s)); } }
Default method: andThen
If the parameters and return values of a method are all of Consumer type, the effect can be achieved: when consuming data, first do an operation, and then do an operation to realize combination. This method is the default method andThen in the Consumer interface. The following is the source code of JDK:
default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) ‐> { accept(t); after.accept(t); }; }
Predict interface
Sometimes we need to judge some type of data to get a boolean value result. You can use Java util. function. Predict interface.
Abstract method: the test Predicate interface contains an abstract method: Boolean test (T). Scenarios for condition judgment:
import java.util.function.Predicate; public class Demo09 { private static void method(Predicate<String> predicate) { boolean veryLong = predicate.test("HelloWorld"); System.out.println("Is the string long:" + veryLong); } public static void main(String[] args) { method(s -> s.length() > 5); } }
The criterion of condition judgment is the incoming Lambda expression logic. As long as the string length is greater than 5, it is considered to be very long.
Default method: and
Since it is conditional judgment, there will be three common logical relationships: and, or and non. The default method and can be used when two Predicate conditions are connected with and logic to achieve the effect of "and". Its JDK source code is:
default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) ‐> test(t) && other.test(t); }
Default method: or
Similar to the and of and, the default method or implements the or in the logical relationship. The JDK source code is:
default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) ‐> test(t) || other.test(t); }
Default method: negate
The "and" and "or" have been understood, and the remaining "non" (negative) will be simple. The JDK source code of the default method negate is:
default Predicate<T> negate() { return (t) ‐> !test(t); }
Function interface
java. util. function. The function < T, R > interface is used to obtain another type of data from one type of data. The former is called pre condition and the latter is called post condition.
Abstract method: apply
The main abstract method in the Function interface is R apply(T t), which obtains the result of type R according to the parameters of type T. Use scenarios such as converting String type to Integer type.
import java.util.function.Function; public class Demo10{ private static void method(Function<String, Integer> function) { int num = function.apply("10"); System.out.println(num + 20); } public static void main(String[] args) { method(s -> Integer.parseInt(s)); } }
Default method: andThen
There is a default andThen method in the Function interface, which is used for combination operations. JDK source code, such as:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) ‐> after.apply(apply(t)); }