The first time I was exposed to Lambda expressions was in TypeScript (the superset of JavaScript), and it was to make the TypeScript's method outside of this method rather than in this method. After using it, I suddenly realized that Lambda is not a heavyweight new feature of JDK8? So I felt that I checked the relevant information and recorded it:
1. Behavior parameterization
In short, behavior parameterization means that the body of the function only contains template-like general code, while some logic that changes with the business scenario is passed into the function in the form of parameters. Using behavior parameterization can make the program more general to cope with the needs of frequent changes.
Consider a business scenario, assuming that we need to filter Apple through a program, we first define an Apple entity:
public class Apple {/** Number*/private long id;/** Color*/private Color color;/** Weight*/private float weight;/** Origin*/private String origin;public Apple() {}public Apple(long id, Color color, float weight, String origin) {this.id = id;this.color = color;this.weight = weight;this.origin = origin;}// Omit getter and setter}The initial demand of users may be simply to filter out green apples through the program, so we can quickly implement them through the program:
public static List<Apple> filterGreenApples(List<Apple> apples) {List<Apple> filterApples = new ArrayList<>();for (final Apple apple : apples) {if (Color.GREEN.equals(apple.getColor())) {filterApples.add(apple);}}return filterApples;}This code is simple and nothing worth saying. But when the user needs become green, it seems that modifying the code is simple, it is nothing more than changing the green of the judgment condition to red. But we need to consider another issue. If the changing conditions change frequently, this is done? If it is just a color change, then we can directly let the user pass in the color judgment conditions, and the parameters of the judgment method change "the set to be judged and the color to be filtered". But what if the user not only judges the color, but also wants to judge weight, size, etc.? Do you think we can just add different parameters to complete the judgment? But is it really good to pass parameters like this? If there are more and more filtering conditions and the combination mode becomes more and more complex, do we need to consider all situations and have corresponding coping strategies for each situation? At this time, we can parameterize the behavior, extract the filtering conditions and pass them in as parameters. At this time, we can encapsulate a judgment interface:
public interface AppleFilter {/*** Filter abstract** @param apple* @return*/boolean accept(Apple apple);}/*** Encapsulate filters into interface** @param apples* @param filter* @return*/public static List<Apple> filterApplesByAppleFilter(List<Apple> apples, AppleFilter filter) {List<Apple> filterApples = new ArrayList<>(); for (final Apple apple : apples) {if (filter.accept(apple)) {filterApples.add(apple);}}return filterApples;}After the above behavior abstraction, we can set filter conditions in the specific call and pass the conditions as parameters into the method. At this time, we use the method of anonymous inner class:
public static void main(String[] args) {List<Apple> apples = new ArrayList<>();// Filter Apple List<Apple> filterApples = filterApplesByAppleFilter(apples, new AppleFilter() {@Overridepublic boolean accept(Apple apple) {// Filter red apples with weight greater than 100g return Color.RED.equals(apple.getColor()) && apple.getWeight() > 100;}});}This design is often used within jdk, such as Java.util.Comparator, java.util.concurrent.Callable, etc. When using this type of interface, we can use anonymous classes to specify the specific execution logic of the function in the specific call places. However, from the above code block, although it is very geek, it is not concise enough. In Java8, we can simplify it through lambda:
// Filter Apple List<Apple> filterApples = filterApplesByAppleFilter(apples,(Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);//()->xxx () is the method parameters, and xxx is the method implementation
2. Lambda expression definition
We can define a lambda expression as a concise and passable anonymous function. First of all, we need to make it clear that the lambda expression is essentially a function. Although it does not belong to a specific class, it has a parameter list, function body, return type, and the ability to throw exceptions; secondly, it is anonymous, and the lambda expression does not have a specific function name; the lambda expression can be passed like parameters, thereby greatly simplifying the writing of code. The format definition is as follows:
Format 1: Parameter List -> Expression
Format 2: Parameter list -> {Expression collection}
It should be noted that the lambda expression implies the return keyword, so in a single expression, we do not need to explicitly write the return keyword, but when the expression is a collection of statements, we need to explicitly add return and enclose multiple expressions with curly braces {}. Let’s see a few examples below:
//Return the length of the given string, implicitly return statement (String s) -> s.length() // Always return the unargumented method() () -> 42 // Contains multiple lines of expression, enclosed in curly braces (int x, int y) -> {int z = x * y;return x + z;}3. Use lambda expressions based on functional interfaces
The use of lambda expressions requires the help of functional interfaces, which means that only when the functional interface appears can we simplify them with lambda expressions.
Custom functional interface:
Functional interfaces are defined as interfaces that have only one abstract method. The improvement in interface definition of java8 is to introduce default methods, so that we can provide default implementations of methods in the interface. However, no matter how many default methods exist, as long as one has and only one abstract method, it is a functional interface, as follows (refer to the above AppleFilter):
/*** Apple filter interface*/@FunctionalInterfacepublic interface AppleFilter {/*** Filter condition abstract** @param apple* @return*/boolean accept(Apple apple);}AppleFilter only contains an abstract method accept (Apple apple). According to the definition, it can be regarded as a functional interface. When defining, we add the @FunctionalInterface annotation to the interface to mark the interface as a functional interface. However, this interface is optional. After adding this interface, the compiler restricts the interface to only have an abstract method, otherwise an error will be reported, so it is recommended to add this annotation to the functional interface.
jdk comes with functional interface:
jdk is a lambda expression with rich functional interfaces. Below are examples of usage of Predicate<T>, Consumer<T>, Function<T, R> respectively.
Predicate:
@FunctionalInterfacepublic interface Predicate<T> {/*** Evaluates this predict on the given argument.** @param t the input argument* @return {@code true} if the input argument matches the predict,* otherwise {@code false}*/boolean test(T t);}The function of Predicate is similar to the AppleFilter above. It uses the conditions we set externally to verify the incoming parameters and returns the verification result boolean. The following uses Predicate to filter the elements of the List collection:
/**** @param list* @param predicate* @param <T>* @return*/public <T> List<T> filter(List<T> list, Predicate<T> predicate) {List<T> newList = new ArrayList<T>();for (final T : list) {if (predicate.test(t)) {newList.add(t);}}return newList;}use:
demo.filter(list, (String str) -> null != str && !str.isEmpty());
Consumer
@FunctionalInterfacepublic interface Consumer<T> {/*** Performs this operation on the given argument.** @param t the input argument*/void accept(T t);}Consumer provides an accept abstract function that receives parameters but does not return values. The following uses Consumer to traverse the collection.
/*** traversal of the collection and perform custom behavior** @param list* @param consumer* @param <T>*/public <T> void filter(List<T> list, Consumer<T> consumer) {for (final T t : list) {consumer.accept(t);}}Using the above functional interface, iterate through the string collection and print non-empty strings:
demo.filter(list, (String str) -> {if (StringUtils.isNotBlank(str)) {System.out.println(str);}});Function
@FunctionalInterfacepublic interface Function<T, R> {/*** Applies this function to the given argument.** @param t the function argument* @return the function result*/R apply(T t);}Function performs a conversion operation. The input is data of type T and returns data of type R. The following uses Function to convert the set:
public <T, R> List<R> filter(List<T> list, Function<T, R> function) {List<R> newList = new ArrayList<R>(); for (final T t : list) {newList.add(function.apply(t));}return newList;}other:
demo.filter(list, (String str) -> Integer.parseInt(str));
The above functional interfaces also provide some default implementations of logical operations. Let's talk about it later when introducing the default methods of Java8 interface~
Some things to pay attention to during use:
Type inference:
During the encoding process, sometimes we may wonder which functional interface our calling code will match. In fact, the compiler will make correct judgments based on parameters, return type, exception type (if present), etc.
When calling in specific ways, the type of parameter can be omitted at some times, thereby further simplifying the code:
// Filter Apple List<Apple> filterApples = filterApplesByAppleFilter(apples,(Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);// In some cases, we can even omit parameter types, and the compiler will correctly judge List<Apple> filterApples = filterApplesByAppleFilter(apples,apple -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);
Local variables
All the examples above Our lambda expressions use their body parameters, we can also use local variables in lambda, as follows
int weight = 100;List<Apple> filterApples = filterApplesByAppleFilter(apples,apple -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= weight);:
In this example, we use the local variable weight in the lambda. However, using local variables in the lambda must require the variable to be explicitly declared as final or in fact final. This is mainly because the local variable is stored on the stack, and the lambda expression is run in another thread. When the thread view accesses the local variable, the variable has the possibility of being changed or recycled, so there will be no thread safety problems after modification with final.
IV. Method citation
Using method references can make the code more simplified. Sometimes this simplification makes the code look more intuitive. Let’s take a look at an example:
/* ... Omit the initialization operation of apples*/// Use the lambda expression apples.sort((Apple a, Apple b) -> Float.compare(a.getWeight(), b.getWeight()));// Use the method to refer to apples.sort(Comparator.comparing(Apple::getWeight));
Method references connect method membership and method itself through::, which are mainly divided into three categories:
Static method
(args) -> ClassName.staticMethod(args)
Convert to
ClassName::staticMethod
Example method of parameter
(args) -> args.instanceMethod()
Convert to
ClassName::instanceMethod // ClassName is a type of args
External instance method
(args) -> ext.instanceMethod(args)
Convert to
ext::instanceMethod(args)
refer to:
http://www.codeceo.com/article/lambda-of-java-8.html
The above is the Lambda expression of the new JDK8 feature that the editor introduced to you. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. Thank you very much for your support to Wulin.com website!