Lambda expression is the most significant new Java language feature since Java SE 5 introduced generics, this article is an article in the last issue of Java Magazine in 2012, which introduces LAMDBA's design intent, application scenarios, and basic syntax.
The lambda expression, chosen by the Expert group of the project, describes a new functional programming structure that is eagerly awaited by the new features that will appear in Java SE 8. Sometimes you will also hear the use of terms such as closures, function direct amounts, anonymous functions, and Sam (Single Abstract Method). Some of these terms are slightly different from each other, but basically they all refer to the same functionality.
Although the lambda expression looks strange at first, it is easy to master. And in order to write applications that can take full advantage of modern multicore CPUs, mastering lambda expressions is critical.
A key concept to keep in mind is that a lambda expression is a small function that can be passed as data. The second concept that needs to be mastered is to understand how the collection object is traversed internally, which is different from the currently existing external sequential traversal.
In this article, we'll show you the motivations behind the lambda expression, the application example, and, of course, its syntax.
Why do you need a lambda expression
There are three main reasons programmers need lambda expressions:
1. More compact Code
2. Ability to modify the functionality of the method by providing additional functionality
3. Better support for multicore processing
More compact Code
Lambda expressions implement a Java class that has only one method in a concise way.
For example, if you have a large number of anonymous inner classes in your code – such as listeners and processor implementations for UI applications, and callable and runnable implementations in concurrent applications – after using lambda expressions, the code becomes very short and easier to understand.
Ability to modify methods
Sometimes the method does not have some of the features we want. For example, the contains () method in the collection interface returns true only if the incoming object does exist in the collection object. But we cannot intervene in the function of this method, for example, if a different casing scheme can be used to think that the string being looked up exists in this collection object, we hope that the contains () method will return true at this point.
To put it simply, what we'd like to do is "pass our own new code into" the existing method, and then call the code passed in. Lambda expressions provide a good way to represent the code that is passed into an existing method and should also be recalled.
Better support for multicore processing
Today's CPUs have multiple cores. This means that multithreaded routines can really be executed in parallel, which is completely different from using time sharing in a single-core CPU. By supporting functional programming syntax in Java, lambda expressions can help you write simple code to effectively apply these CPU cores.
For example, you can manipulate large collection objects in parallel, using parallel programming patterns, such as filtering, mapping, and simplifying (which will soon be exposed to these patterns), to use all available hardware threads in the CPU.
Lambda expression Overview
In the previous example of finding a string using a different casing scheme, what we want to do is to pass the representation of the method toLowerCase () as the second parameter into the contains () method, which requires the following work:
1. Find a way to treat code snippets as a value (an object)
2. Find a way to pass the code snippet above to a variable
In other words, we need to wrap a program logic into an object, and the object can be passed. To be more specific, let's look at two examples of basic lambda expressions that can be replaced by existing Java code.
Filter
The code snippet you want to pass may be a filter, which is a good example. For example, suppose you are using Java.io.FileFilter (in Java SE 7 preview) to determine whether a directory is subordinate to a given path, as shown in Listing 1,
Listing 1
File dir = new file ("/an/interesting/location/"); FileFilter directoryfilter = new FileFilter () {public Boolean accept (file file) { return file.isdirectory ();
} }; file[] directories = Dir.listfiles (directoryfilter);
After using a lambda expression, the code is greatly simplified, as shown in Listing 2.
Listing 2
File dir = new file ("/an/interesting/location/"); FileFilter Directoryfilter = (File f), F.isdirectory (); file[] directories = Dir.listfiles (directoryfilter);
The left side of the assignment expression infers the type (FileFilter), and the right side looks like a smaller version of the Accept () method in the FileFilter interface, which accepts a file object and returns a Boolean value after the decision F.isdirectory ().
In fact, because lambda expressions take advantage of type derivation, we can further simplify the code above, based on how it works later. The compiler knows that FileFilter has only the only method accept (), so it must be the implementation of the method. We also know that the accept () method requires only one parameter of type file. Therefore, F must be of type file. As shown in Listing 3,
Listing 3
File dir = new file ("/an/interesting/location/"); file[] directories = Dir.listfiles (f-f.isdirectory ());
As you can see, using lambda expressions can drastically reduce the number of template code.
Once you get used to using lambda expressions, it makes the logic flow very easy to read. One of the key ways to achieve this is to place the filtering logic on the side of the method that uses the logic.
Event handlers
A UI program is another area where anonymous internal classes are heavily used. Let's assign a click Listener to a button, as shown in Listing 4,
Listing 4
Button button = New button (); Button.addactionlistener (new ActionListener () {public void actionperformed (ActionEvent e) { Ui.showsomething (); } });
This is nothing more than saying "call this method when you click the button." Using a lambda expression, you can write the code shown in Listing 5.
Listing 5
ActionListener Listener = event, {ui.showsomething ();}; Button.addactionlistener (listener);
The listener can be reused if necessary, but if it needs to be used only once, the code in Listing 6 considers a good way.
Listing 6
Button.addactionlistener (event, {ui.showsomething ();});
In this case, the syntax for using the extra curly braces is somewhat odd, but this is necessary because the actionperformed () method returns void. We'll see more about that later.
Now let's focus instead on the role that lambda expressions play in writing modern code that handles collection objects, especially when it comes to translating between two programming styles, external traversal and internal traversal.
External traversal vs. internal traversal
So far, the standard way to handle Java collection objects is through external traversal. This is called an external traversal because the control flow outside the collection object is used to iterate through the elements contained in the collection. This traditional way of handling collections is well known to most Java programmers, although they do not know or use the term external traversal.
As shown in Listing 7, the Java language constructs an external iterator for the enhanced for loop and uses this iterator to iterate through the collection object.
Listing 7
list<string> mystrings = Getmystrings (); for (String mystring:mystrings) { if (mystring.contains (possible)) System.out.println (myString + "contains" + possible); }
Using this method, the collection class represents a "whole" view of all the elements, and the collection object can also support random access to arbitrary elements, which the programmer might have.
Based on this view, the collection object can be traversed by calling the iterator () method, which returns an iterator to the collection element type, which is a more restrictive view of the same collection object. It does not expose any interfaces for random access; instead, it is designed purely for sequential access to collection elements. This ordering nature makes the infamous concurrentmodificationexception when you try to access the collection object concurrently.
Another alternative is to require that the collection object be able to manage iterators (or loops) internally, which is an internal traversal that takes precedence over internal traversal when using lambda expressions.
In addition to the new lambda expression syntax, the lambda project includes a massively upgraded collection Framework class library. The purpose of this upgrade is to make it easier to write code that uses internal traversal to support a well-known set of functional programming paradigms.
Functional programming with Lambda
Once, most developers found that they needed collections to do one or more of the following:
1. Create a new collection object, but filter out elements that do not meet the criteria.
2. Convert the elements in the collection to each other and use the converted collection.
3. Create an overall value for an attribute of all elements in the collection, for example, totals and averages. Such tasks, called filtering, mapping, and simplification, have common points: they all need to work with each element in the collection.
The key principle is that the program must process every element in the collection, whether it is determining whether an element exists or whether it is conforming to a certain condition (filtering), or converting an element into a new element and generating a new set (mapping), or calculating the overall value (simplification).
This implies that we need a simple way to represent the program used for internal traversal. Fortunately, Java SE 8 provides building block statements for this class of notation.
Java SE 8 classes that support basic functional programming
Some of the classes in Java SE 8 are intended to be used to implement the aforementioned functional paradigms, including Predicate,mapper and block–, and of course, there are other classes – all in a new java.util.functions package.
Look at the more details of the predicate class, which is often used to implement a filtering algorithm, and it acts on a collection to return a new collection containing elements that conform to the predicate condition. There are many interpretations of what is a predicate. Java SE 8 considers a predicate to be a method of determining true or false based on the value of its variable.
Consider one of the examples we have seen before. Given a collection of strings, we want to determine whether it contains the specified string, but want the comparison of strings to be case insensitive.
In Java SE 7, we will need to use an external traversal whose code is shown in Listing 8.
Listing 8
public void Printmatchedstrings (list<string> mystrings) { list<string> out = new arraylist<> (); C2/>for (String s:mystrings) { if (s.equalsignorecase (possible)) Out.add (s); } Log (out); }
In the upcoming Java SE 8, we can write more compact programs using predicate and a new helper method (filter) in the collections class, as shown in Listing 9.
Listing 9
public void Printmatchedstrings () { predicate<string> matched = S-s.equalsignorecase (possible); Log (Mystrings.filter (matched)); }
In fact, if you use a more general functional programming style, you only need to write a single line of code, as shown in Listing 10,
Listing 10
public void Printmatchedstrings () { log (Mystrings.filter (S-s.equalsignorecase (possible)));
As you can see, the code is still very readable, and we appreciate the benefits of using internal traversal.
Finally, let's discuss the more details of the lambda expression syntax.
Syntax rules for lambda expressions
The basic format of a lambda expression is to start with an acceptable argument list, ending with some code (called the expression Body/body), and separating the first two with an arrow (-).
Note: The syntax of the lambda expression may still be changed, but the syntax shown in the following example is working correctly at the time of writing this article.
Lambda expressions rely heavily on type derivation, which is extremely unusual compared to the rest of Java's syntax.
Let's take a closer look at an example we've seen before (see Listing 11). If you look at the definition of ActionListener, you can see that it has only one method (see listing 12).
Listing 11
ActionListener Listener = event, {ui.showsomething ();};
Listing 12
Public interface ActionListener {public void actionperformed (ActionEvent event); }
Therefore, the lambda expression on the right side of listing 11 can easily be understood as "this is a method definition for an interface that declares only a single method." Note that you still have to follow the general rules for Java static types, which is the only way to make type inference work correctly.
As a result, you can use lambda expressions to convert the previously written anonymous inner class code to more compact code.
You also need to be aware of another weird grammar. Let's review the example above, as shown in Listing 13,
Listing 13
FileFilter Directoryfilter = (File f), F.isdirectory ();
At a glance, it looks similar to the ActionListener example, but let's look at the definition of the FileFilter interface (see listing 14). The Accept () method returns a Boolean value, but does not have an explicit return statement. Instead, the type of the return value is derived from the lambda expression
Listing 14
Public interface FileFilter {public Boolean accept (File pathname); }
This explains why special handling is required when the return type of the method is void. In this case, the lambda expression uses a pair of extra parentheses to wrap the code part (expression body/body). Without this weird syntax, type deduction will not work – but you have to understand that this syntax can be changed.
The expression body of a lambda expression can contain multiple statements, in which case the expression body needs to be surrounded by parentheses, but the syntax for the return type of the "pushed export" will not function, then the return type keyword is necessary.
Finally, it's important to remind you that the IDE does not seem to support lambda syntax at this time, so when you first try a lambda expression, you have to pay extra attention to any warnings that the Javac compiler throws.
Conclusion
Lambda expressions are the most significant new features of the Java language since Java SE 5 introduced generics. Properly applied, lambda expressions allow you to write concise code, add additional functionality to existing methods, and better adapt to multicore processors.
The Java language and lambda expressions in the JVM