My view of lambda expressions in Java is quite tangled:
One I think: lambda expressions reduce the reading experience of Java programs. Java programs are not always expressive, but one of the things that makes Java popular is its security and conservatism-even beginners can write robust and easily maintainable code if they just pay attention. Lambda expressions are a relatively high level of demand for developers, and thus add some maintenance difficulties.
Another thing I think: As a code, it is necessary to learn and accept the new features of the language. If only because of its poor reading experience to give up its strength in expressiveness, then even the three-eye expression also some people find it difficult to understand. Language is also in development, can not keep up with the voluntary be left behind.
I don't want to be left behind. But if I had to make a choice, my decision was conservative: there was no need to use lambda--in the Java language. It makes many people in the current Java circle unaccustomed to the rise in human costs. If you really like it, you can consider using Scala.
Anyway, I started to try to master the lambda, after all, some of the code maintained in the work uses a lambda (believe me, I'll take it out gradually). The course of study is a related tutorial at Oracla–java official website.
——————————
Suppose you are currently creating a social network application. One of the features is that an administrator can perform certain actions, such as sending a message, on a member that meets a specified condition. The following table describes this use case in detail:
Field |
Describe |
Name |
The action to perform |
Key participants |
Administrator |
Prerequisite conditions |
Administrator Login System |
Post condition |
Perform actions only on members who meet specified criteria |
Master Success Scenario |
1. The administrator sets the filtration standard for the target member to perform the operation; 2. The administrator selects the action to be performed; 3. Admin click submit button; 4. The system to find members that meet the specified criteria; 5. The system performs a preselected operation on the member who meets the specified criteria. |
Extended |
The administrator can choose whether to preview the membership information that meets the filtering criteria before choosing to perform the action or clicking the Submit button. |
Frequency of Occurrence |
There will be many times in a day. |
Use the person class below to represent membership information in a social network:
public class Person {public
enum Sex {
MALE, FEMALE
}
String name;
Localdate birthday;
Sex gender;
String EmailAddress;
public int getage () {
//...
}
public void Printperson () {
//...
}
}
Assume that all members are saved in a list<person> instance.
In this section we start with a very simple method, and then try to use the local class and the anonymous class to implement it, and in the end we will step into the powerful and efficient lambda expression. You can find the complete code here.
Scenario One: Create a way to find members that meet the specified criteria
This is the simplest way to implement the previous mentioned cases: to create several methods, each method to verify a standard (such as age or gender). The following code verifies that the age is greater than a specified value:
public static void Printpersonsolderthan (List<person> roster, Int. int) {for
(person P:roster) {
if (P.G Etage () >= age) {
P.printperson ();}}}
This is a very fragile scenario, and it is highly likely that the application will not run because of a little update. If we add new member variables to the person class or change the algorithm for measuring age in the standard, we need to rewrite a lot of code to accommodate this change. Moreover, the restrictions are too rigid, say we want to print the age of less than a specified value of the members how to do? Add a new method Printpersonsyoungerthan? This is obviously a stupid method.
Scenario Two: Creating a more general approach
The following method is more adaptable than Printpersonsolderthan; This method prints the membership information for a specified age:
public static void Printpersonswithinagerange (
list<person> Roster, int-low, int high) {for
(person P:ros ter) {
if (<= p.getage () && p.getage () < high) {
P.printperson ();}
}
}
Now there's a new idea: what if we want to print membership information for a specific sex, or a member with a specified sex and a specified age? What if we adjust the person class and add attributes such as friendliness and geography? Although this method of writing is a bit more versatile than Printpersonsyoungerthan, writing a method for every possible query can also cause the code to be vulnerable. It's better to check this piece of code to separate it into a new class.
Scenario Three: Implement standard checking in a local class
The following method prints the membership information that meets the criteria for the search:
public static void Printpersons (List<person> roster, Checkperson Tester) {to
(person P:roster) {
if (tes Ter.test (P)) {
P.printperson ();}}}
A Checkperso object tester is used in the program to validate each instance in the list parameter roster. If Tester.test () returns True, the Printperson () method is executed. In order to set the search criteria, the Checkperson interface needs to be implemented.
The following class implements the Checkperson and provides a concrete implementation of the test method. The test method in this class filters the membership information that satisfies the conditions of military service in the United States: sex is male, and the age is between 18~25岁.
Class Checkpersoneligibleforselectiveservice implements Checkperson {public
boolean test [person p] {
return P.gender = = Person.Sex.MALE &&
p.getage () >= &&
p.getage () <=
To use this class, you need to create an instance and trigger the Printpersons method:
Printpersons (
roster, new Checkpersoneligibleforselectiveservice ());
The code now looks less vulnerable-we don't need to rewrite the code because of the change in the person class structure. However, there is still additional code: a newly defined interface that defines an internal class for each search criterion in the application.
Because Checkpersoneligibleforselectiveservice implements an interface, you can use anonymous classes without having to define an internal class for each of the criteria.
Scenario four: Using anonymous classes to implement standard checks
One of the parameters in the Printpersons method called below is an anonymous class. The function of this anonymous class is the same as in the Checkpersoneligibleforselectiveservice class in scenario three: it is a member who filters gender and is between 18 and 25 years of age.
Printpersons (
roster,
new Checkperson () {public
Boolean test (person p) {return
p.getgender () = = Person.Sex.MALE
&& p.getage () >=
&& p.getage () <=;
}
);
This scheme reduces the amount of coding because you no longer need to create new classes for each retrieval scenario that you want to perform. But this is still a bit uncomfortable: Although the Checkperson interface has only one method, the anonymous class implemented is still somewhat cumbersome. You can use a lambda expression to replace an anonymous class, and here's how to replace an anonymous class with a lambda expression.
Scenario Five: Using lambda expressions to implement standard checks
The Checkperson interface is a functional interface. The so-called functional interface means any interface that contains only one abstract method. (You can also have multiple default methods or static methods in a functional interface.) Since there is only one abstract method in a functional interface, the method name can be omitted from the method when implementing the method of the functional interface. To implement this idea, you can use a lambda expression to replace an anonymous class expression. In the Printpersons method rewritten below, the relevant code is highlighted:
Printpersons (
roster,
(person P)-> p.getgender () = = Person.Sex.MALE
&& p.getage () >= 18
&& p.getage () <=
);
You can also use a standard function interface to replace the Checkperson interface to further simplify the code.
Scenario Six: Using a standard functional interface in a lambda expression
Let's take a look at the Checkperson interface:
Interface Checkperson {
boolean test (person P);
}
This is a very simple interface. Because there is only one abstract method, it is also a functional interface. This abstract method takes only one argument and returns a Boolean value. This abstract interface is so simplistic that we consider whether it is necessary to define an interface in the application. You can consider using the standard functional interface defined by JDK to find these interfaces under the Java.util.function package.
In this example, we can use the Predicate<t> interface to replace Checkperson. There is a Boolean test (T-T) method in this interface:
Interface Predicate<t> {
boolean test (t);
}
The Predicate<t> interface is a generic interface. A generic class (or a generic interface) specifies one or more type parameters using a pair of angle brackets (<>). There is only one type parameter in this interface. When you declare or instantiate a generic class by using a specific class, you get a parameterized class. For example parameterized class predicate<person> is like this:
Interface Predicate<person> {
boolean test (Person T);
}
There is a method in this parameterized class that is consistent with the parameters and return values of the Checkperson.boolean test (person p) method. You can therefore use the Predicate<t> interface to replace the Checkperson interface as demonstrated by the following methods:
public static void Printpersonswithpredicate (
list<person> roster, predicate<person> tester) {
for (person P:roster) {
if (Tester.test (p)) {
P.printperson ();
}
}
}
You can then use the following code to screen the members of the age of military service as shown in scenario three:
Printpersonswithpredicate (
roster,
P-> p.getgender () = = Person.Sex.MALE
&& p.getage () >=
&& p.getage () <=
);
Have you noticed that when you use predicate<person> as the parameter type, you do not explicitly specify the parameter type. This is not the only place to apply lambda expressions, and the following scenario describes the use of more lambda expressions.
Programme VII: Using lambda expressions throughout the application
Take a look at the Printpersonswithpredicate method and consider whether lambda expressions can be used here:
public static void Printpersonswithpredicate (
list<person> roster, predicate<person> tester) {
for (person P:roster) {
if (Tester.test (p)) {
P.printperson ();
}
}
}
Use the predicate instance tester in this method to examine each person instance in the roster. If the person instance conforms to the check criteria defined in tester, the Printperson method of the person instance is triggered.
In addition to triggering the Printperson method, the person instance that satisfies the tester standard can perform other methods. You can consider using a lambda expression to specify the method to execute (this is a good feature, which solves the problem that methods in Java cannot be passed as objects). Now you need a lambda expression like the Printperson method--a lambda expression that requires only one argument and returns to void. Remember one point: to use a lambda expression, you need to implement a functional interface first. In this example, a functional interface is required, which contains only an abstract method, which has a parameter of type person and returns to void. You can look at the standard functional interface consumer<t> that the JDK provides, and it has an abstract method void accept (T) that satisfies this requirement. Use an instance of consumer<t> in the following code to invoke the Accept method to replace the P.printperson ():
public static void Processpersons (
list<person> roster,
predicate<person> tester,
Consumer <person> Block] {for
(person P:roster) {
if (Tester.test (p)) {
block.accept (p);
}
}}
For this, you can use the following code to filter the members of the Age of military service:
Processpersons (
roster,
P-> p.getgender () = = Person.Sex.MALE
&& p.getage () >=
& & P.getage () <=,
P-> P.printperson ()
);
If what we want to do is not just print membership information, but more things, such as verifying membership, obtaining membership contacts, and so on. At this point, we need a functional interface with the return value method. The standard functional interface function<t,r> of JDK has one such method R apply (t). The following method takes the data from the parameter mapper and performs the behavior specified by the parameter block on the data:
public static void Processpersonswithfunction (
list<person> roster,
predicate<person> tester,
Function<person , string> Mapper,
consumer<string> block) {for
(person P:roster) { C15/>if (Tester.test (p)) {
String data = mapper.apply (p);
Block.accept (data);}}
The following code obtains the mailbox information of all members of the roster in the age of military service and prints it out:
Processpersonswithfunction (
roster,
P-> p.getgender () = = Person.Sex.MALE
&& p.getage () >=
&& p.getage () <=,
P-> p.getemailaddress (),
email-> System.out.println (email)
);
Scenario VIII: Multi-use generics
Review the Processpersonswithfunction method again. The following is a generic version of this method, and the new method requires more tolerance on the parameter types:
public static <x , y> void processelements (
iterable<x> source,
predicate<x> tester,
function<x , y> Mapper,
consumer<y> block} {for
(x p:source) {
if tester.test ( p)) {
Y data = mapper.apply (p);
Block.accept (data);}}
To print membership information for age-appropriate military service you can call the Processelements method as follows:
Processelements (
roster,
P-> p.getgender () = = Person.Sex.MALE
&& p.getage () >=
&& p.getage () <=,
P-> p.getemailaddress (),
email-> System.out.println (email)
;
The following behavior was performed during the invocation of the method:
Gets the object information from a collection, in this case retrieving the person object information from the collection instance roster.
Filtering objects that can match predicate instance tester. In this example, the predicate object is a lambda expression that specifies the conditions for filtering the age of military service.
The filtered object is handed to a Function object mapper processing, and mapper matches a value for that object. In this example, the Function object Mapper is a lambda expression that returns the email address of each member.
Specifies a behavior for values that are matched by the consumer object block for mapper. In this example, the consumer object is a lambda expression that prints a string, which is the member e-mail address returned by the function instance Mapper.
Scenario Nine: Using a clustered operation that uses a lambda expression as a parameter
The following code uses a clustered operation to print the e-mail address of a member of the age of military service in the Roster collection:
Roster.stream ()
. Filter (
P-> p.getgender () = = Person.Sex.MALE
&& p.getage () >=
&& p.getage () <=
. Map (P-> p.getemailaddress ())
. ForEach (Email-> System.out.println ( email));
Analyze the execution process of the code as above, and organize the following table:
Behavior |
Clustered operations |
Get Object |
Stream<e> Stream () |
Filters for objects that match the specified criteria for the predicate instance |
stream<t> filter (PREDICATE< super t> predicate) |
Gets the value of an object match through a function instance |
<R> stream<r> Map (function< Super T,? Extends r> mapper) |
Perform the behavior specified by the consumer instance |
void ForEach (consumer<? Super t> Action) |
The
filter, map, and foreach operations in a table are clustered operations. The elements processed by the clustered operation come from the stream, not directly from the collection (simply because the first method invoked in the sample program is stream ()). A stream is a sequence of data. Unlike collections, stream does not store data in a particular structure. Instead, the stream fetches data from a particular source, such as fetching data from a collection, through a pipeline. Pipeline is a sequence of stream operations, in this case the Filter-map-foreach. In addition, the aggregation operation usually takes a lambda expression as an argument, which also gives us a lot of custom space.