Java 8 lambda expression One look at it.

Source: Internet
Author: User
Tags closure iterable

One problem with anonymous inner classes is that the implementation of an anonymous inner class is very simple, such as an interface There is only one abstract function, then the syntax of the anonymous inner class is a bit clumsy and unclear. We often have the actual need to pass a function as a parameter to another function, such as when a button is clicked, we need to set the button response function to the button object. A lambda expression can take a function as a function parameter, and the code (function) as a data (parameter), which satisfies the above requirements. Using lambda expressions can be more flexible when you want to implement an interface that has only one abstract function.

One use case for using lambda expressions

Let's say you're creating a social networking app. You should now develop a function that allows administrators to do various things for the user, such as searching, printing, getting mail, and so on. Assume that a user of a social network application is represented by a Person class:

public class Person {    public enum Sex {        MALE, FEMALE    }    private String name;    private LocalDate birthday;    private Sex gender;    private String emailAddress;    public int getAge() {        // ...    }    public void printPerson() {        // ...    }}

Assume that all users of a social network application are saved in one List<Person> instance.

We first use a simple method to implement this use case, and then by using the local class, Anonymous inner class implementation, finally through the lambda expression to do an efficient and concise implementation.

Method 1: Create a method that queries matching users based on an attribute

The simplest way is to create several functions, each of which searches for a specified user trait, such as searchByAge() this method, and the following method prints all users older than a certain value:

public static void printPersonsOlderThan(List<Person> roster, int age) {    for (Person p : roster) {        if (p.getAge() >= age) {            p.printPerson();        }    }}

There is a potential problem with this approach, and if a change is introduced (such as a new data type) The program will go wrong. Suppose that the application has been updated and the classes have changed Person , such as replacing the age with the birth date, or the algorithm for searching the age may be different. This way you won't be writing many APIs to adapt to these changes.

Method 2: Create a more general search method

This method printPersonsOlderThan is more generic than it is, and it provides users who can print an age range:

public static void printPersonsWithinAgeRange(    List<Person> roster, int low, int high) {    for (Person p : roster) {        if (low <= p.getAge() && p.getAge() < high) {            p.printPerson();        }    }}

What if you want to print a specific gender or print a user who meets a specific gender and age range? If you want to change the person class, add other attributes, such as Love status, geographic location? Although this method printPersonsOlderThan is more generic than the method, each query creates a specific function that can cause the program to be less robust. You can use interfaces to transfer specific searches to specific classes that need to be searched (the idea of interface-oriented programming-simple Factory mode).

Method 3: Set a specific search condition in the local class

The following method can print out all user information that matches the search criteria

public static void printPersons(    List<Person> roster, CheckPerson tester) {    for (Person p : roster) {        if (tester.test(p)) {            p.printPerson();        }    }}

This method tester.test detects roster whether the elements in each list satisfy the search criteria by calling the method. If tester.test true, the instance that matches the condition is printed Person .

Implement the search by implementing an CheckPerson interface.

interface CheckPerson {    boolean test(Person p);}

The following class implements the CheckPerson method of the interface test . If Person the attribute is male and the age is between 18-25 years old will return true

class CheckPersonEligibleForSelectiveService implements CheckPerson {    public boolean test(Person p) {        return p.gender == Person.Sex.MALE &&            p.getAge() >= 18 &&            p.getAge() <= 25;    }}

When you want to use this class, you only need to instantiate an instance and pass the instance as a parameter to the printPersons method.

printPersons(roster, new CheckPersonEligibleForSelectiveService());

Although this approach is less fragile- Person you don't need to re-create more methods when things change, but you still need to add some code: a local class for each search criterion to implement the interface. CheckPersonEligibleForSelectiveServiceclass implements an interface, you can use an anonymous inner class instead of a local class to satisfy a different search by declaring a new inner class.

Method 4: Specify the search criteria in the anonymous inner class

The second parameter of the following printPersons function call is an anonymous inner class, this anonymous inner class filters users who meet gender as male and age between 18-25 years:

printPersons(    roster,    new CheckPerson() {        public boolean test(Person p) {            return p.getGender() == Person.Sex.MALE                && p.getAge() >= 18                && p.getAge() <= 25;        }    });

This method reduces the amount of code because you do not have to create a new class for each search criterion. However, given that the CheckPerson interface has only one function, the syntax of the anonymous inner class is somewhat cumbersome. In this case, you might consider replacing the anonymous inner class with a lambda expression, as described in the following.

Method 5: Real-search interfaces via lambda expressions

CheckPersonAn interface is a functional interface . An interface with only one abstract method is a functional interface (a functional interface may also be shifting to a live multiple default or static method). Since the function interface contains only an abstract method, you can omit the name of the method when implementing the method. So you can use a lambda expression instead of an anonymous inner class expression, called as follows:

printPersons(    roster,    (Person p) -> p.getGender() == Person.Sex.MALE        && p.getAge() >= 18        && p.getAge() <= 25);

The syntax for lambda expressions is described in more detail later. You can also use a standard functional interface instead of an CheckPerson interface, which further reduces the amount of code.

Method 6: Use a standard functional interface and a lambda expression

CheckPersonThe interface is a very simple interface:

interface CheckPerson {    boolean test(Person p);}

It has only one abstract method, so it is a functional interface. This function has one parameter and one return value. It's too simple to define it in your app. As a result, some standard functional interfaces are defined in the JDK and can be found in the java.util.function package. For example, you can use Predicate&lt;T&gt; overrides CheckPerson . Only methods are included in this interface boolean test(T t) .

interface Predicate<T> {    boolean test(T t);}

Predicate&lt;T&gt;is a generic interface, generics need to specify one or more parameters in angle brackets (<>). In this interface, only one parameter T is shifting. When you declare or instantiate a generic with a real type parameter, you will get a parameterized type. For example, the parameterized type Predicate&lt;Person&gt; looks like the following code:

interface Predicate<Person> {    boolean test(Person t);}

The parameterized interface contains an interface, which is CheckPerson.boolean test(Person p) exactly the same. Therefore, you can use the substitution as in the following code Predicate&lt;T&gt; CheckPerson :

public static void printPersonsWithPredicate(    List<Person> roster, Predicate<Person> tester) {    for (Person p : roster) {        if (tester.test(p)) {            p.printPerson();        }    }}

Well, you can call this function like this:

printPersonsWithPredicate(    roster,    p -> p.getGender() == Person.Sex.MALE        && p.getAge() >= 18        && p.getAge() <= 25);

This is not the only way to use LAMDBA expressions. It is recommended that you use lambda expressions in the following other ways.

Method 7: Use lambda expressions in your app

Look at the method. printPersonsWithPredicate you can also use lambda expressions:

public static void printPersonsWithPredicate(    List<Person> roster, Predicate<Person> tester) {    for (Person p : roster) {        if (tester.test(p)) {            p.printPerson();        }    }}

This method detects roster Person whether each instance meets tester the criteria. If the person instance satisfies the tester criteria set in, the Person instance information will be printed.

You can specify a different action to perform a print instance that satisfies the tester search criteria defined in Person . You can specify that this action is a lambda expression. Suppose you want a function and printPerson the same lambda expression (a parameter, return void), you need to implement a functional interface. In this case, you need a functional interface that contains only a single person type parameter and a return void. Consumer&lt;T&gt;interface Replacement void accept(T t) of a function, which meets the above requirements. The following function uses the call Consumer&lt;Person&gt; accept() to replace p.printPerson() the call.

public static void processPersons(    List<Person> roster,    Predicate<Person> tester,    Consumer<Person> block) {        for (Person p : roster) {            if (tester.test(p)) {                block.accept(p);            }        }}

Then you can call the function like this processPersons :

processPersons(     roster,     p -> p.getGender() == Person.Sex.MALE         && p.getAge() >= 18         && p.getAge() <= 25,     p -> p.printPerson());

What if you want to do more with the user's information than print it? Suppose you want to verify members ' personal information or get information about their contacts? In this case, you need a functional interface with an abstract function that has a return value. The Function&lt;T,R&gt; interface contains a R apply(T t) method with a parameter and a return value. The following method gets the data that the parameter matches to, and then handles the corresponding lambda expression code block:

public static void processPersonsWithFunction(    List<Person> roster,    Predicate<Person> tester,    Function<Person, String> mapper,    Consumer<String> block) {    for (Person p : roster) {        if (tester.test(p)) {            String data = mapper.apply(p);            block.accept(data);        }    }}

The following function roster obtains the mailbox address of the user who meets the search criteria from, and prints the address.

processPersonsWithFunction(    roster,    p -> p.getGender() == Person.Sex.MALE        && p.getAge() >= 18        && p.getAge() <= 25,    p -> p.getEmailAddress(),    email -> System.out.println(email));
Method 8: Use generics to make it more generic

The processPersonsWithFunction following function can accept a collection that contains any data type:

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);        }    }}

You can call the above function to print the mailbox of a user that matches the search criteria:

processElements(    roster,    p -> p.getGender() == Person.Sex.MALE        && p.getAge() >= 18        && p.getAge() <= 25,    p -> p.getEmailAddress(),    email -> System.out.println(email));

The call to the method executes the following action:

    1. Gets the object from the collection, in this case it is a Person collection of replacement instances roster . rosteris a list type and is also a iterable type.
    2. Filters objects that conform to the Predicate data type tester . In this example, the predicate object is a lambda expression that specifies the criteria that match the search.
    3. Use Function the mapper of the type to map each object that meets the filter criteria. In this example, the function object is to return the user's e-mail address.
    4. Executes an action defined in the object block for each object that is mapped to Consumer . In this example, the consumer object is an LAMDBA expression that prints the e-mail address returned by the function object.

You can replace the above operation with an aggregation operation.

Method 9: Merge operations using a lambda expression as a parameter

The following example uses the aggregation operation to print out the e-mail address of the user who meets the search criteria:

roster    .stream()    .filter(        p -> p.getGender() == Person.Sex.MALE            && p.getAge() >= 18            && p.getAge() <= 25)    .map(p -> p.getEmailAddress())    .forEach(email -> System.out.println(email));

The following table maps the processElements function execution and the corresponding aggregation operation

processelements actions aggregation actions
Get Object source stream<e> Stream ()
filters instances that conform to the predicate object (lambda expression) stream<t> filter (predicate<? Super T> P redicate)
use function object to map objects that conform to the filtering criteria <R> stream<r> ma P (function<? Super T,? Extends r> mapper)
performs a consumer object (lambda expression) set action void ForEach (consumer<? Super t> Action)

filter, map and forEach is an aggregation operation. The aggregation operation is stream handled from within the individual elements, rather than directly from the collection (which is why the first called function is stream() ). Steam is the serialization of individual elements. Unlike collections, it is not a data structure that stores data. Instead, the stream loads the values from the source, such as the collection by pipeline loading the data into the stream. pipelineis a serialized operation of Stream, in this case filter- map-forEach . Also, aggregation operations can typically receive a lambda expression as a parameter, so you can customize the action you want.

Using lambda expressions in GUI programs

In order to handle events in a graphical user interface (GUI) application, such as keyboard input events, mouse movement events, scrolling events, you typically implement a specific interface to create an event handler. Typically, a time-processing interface is a functional interface, which usually has only one function.

The time previously implemented with anonymous inner classes is appropriate:

 btn.setOnAction(new EventHandler<ActionEvent>() {            @Override            public void handle(ActionEvent event) {                System.out.println("Hello World!");            }        });

You can use the following code instead:

 btn.setOnAction(          event -> System.out.println("Hello World!")        );
Lambda expression syntax

A lambda expression consists of a structure:

    • ()If you have multiple arguments, separate them with commas. The CheckPerson.test function has a parameter p, which represents an instance of person.

      Note: You can omit the parameter types from the lambda expression. In addition, parentheses can be omitted if there is only one argument. For example, the following lambda expression is also valid:

p -> p.getGender() == Person.Sex.MALE     && p.getAge() >= 18    && p.getAge() <= 25
    • Arrow symbol:->
    • Body: There is an expression or a declaration block consisting of. In the example, use an expression like this:
p.getGender() == Person.Sex.MALE     && p.getAge() >= 18    && p.getAge() <= 25

If you set an expression, the Java runtime evaluates the expression and eventually returns the result. At the same time, you can use a return declaration:

p -> {    return p.getGender() == Person.Sex.MALE        && p.getAge() >= 18        && p.getAge() <= 25;}

Instead of returning an expression in a lambda expression, you must surround the block of {} code with it. However, parentheses are not required when a type is returned void . For example, the following is also a valid lambda expression:

email -> System.out.println(email)

A lambda expression looks a bit like a declarative function, and you can think of a lambda expression as an anonymous function (a function without a name).

The following is an example of a lambda expression with multiple parameters:

public class Calculator {    interface IntegerMath {        int operation(int a, int b);       }    public int operateBinary(int a, int b, IntegerMath op) {        return op.operation(a, b);    }    public static void main(String... args) {        Calculator myApp = new Calculator();        IntegerMath addition = (a, b) -> a + b;        IntegerMath subtraction = (a, b) -> a - b;        System.out.println("40 + 2 = " +            myApp.operateBinary(40, 2, addition));        System.out.println("20 - 10 = " +            myApp.operateBinary(20, 10, subtraction));        }}

Method operateBinary performs a mathematical operation of two numbers. The operation itself is an instantiation of the IntegerMath class. Two operations, addition and subtraction, are defined in an instance through a lambda expression. The example output is as follows:

40 + 2 = 4220 - 10 = 10
To get a local variable in a closure

Like local classes and anonymous classes, lambda expressions can also access local variables, and they have permission to access local variables. A lambda expression is also part of the current scope, meaning that it does not inherit any named names from the parent scope or introduce a new level of scope . The scope of a lambda expression is the scope where it is declared. This is illustrated in the following example:

import java.util.function.Consumer;public class LambdaScopeTest {    public int x = 0;    class FirstLevel {        public int x = 1;        void methodInFirstLevel(int x) {            Consumer<Integer> myConsumer = (y) ->             {                System.out.println("x = " + x);                 System.out.println("y = " + y);                System.out.println("this.x = " + this.x);                System.out.println("LambdaScopeTest.this.x = " +                    LambdaScopeTest.this.x);            };            myConsumer.accept(x);        }    }    public static void main(String... args) {        LambdaScopeTest st = new LambdaScopeTest();        LambdaScopeTest.FirstLevel fl = st.new FirstLevel();        fl.methodInFirstLevel(23);    }}

The following information will be output:

x = 23y = 23this.x = 1LambdaScopeTest.this.x = 0

If you myConsumer use x instead of parameter y in a lambda expression, as in the following, the compilation will be error-prone.

Consumer<Integer> myConsumer = (x) -> {}

The compilation appears "variable x is already defined in method methodinfirstlevel (int)" Because the lambda expression does not introduce a new scope (the scope of the lambda expression already has x defined). Therefore, you can directly access the member variables, functions, and local variables in the closure of the lambda expression. For example, a lambda expression can directly access the parameter X of the method Methodinfirstlevel. You can use the This keyword to access scope at the class level. In this example, this.x the value of the member variable firstlevel.x.

However, like local and anonymous classes, lambda expression values can access local variables and formal parameters that are decorated as final or effectively final. For example, suppose you methodInFirstLevel add a definition declaration in the following:

Effectively final: The value of a variable or parameter is not changed after initialization, then the variable or parameter is effectively Final type.

void methodInFirstLevel(int x) {    x = 99;}

Because x =99 the declaration makes methodInFirstLevel the parameter x is no longer the effectively final type. As a result, the Java compiler will report an error similar to "Local variables referenced from a lambda expression must is final or effectively final".

Target type

How does Java judge the data type of a lambda expression at run time? Take a look at that. Lambda expression between 18-25 years of age and sex is male:

p -> p.getGender() == Person.Sex.MALE    && p.getAge() >= 18    && p.getAge() <= 25

This lambda expression is passed in the form of a parameter to the following two functions:

    • public static void Printpersons (List<person> roster, Checkperson Tester)
    • public void Printpersonswithpredicate (list<person> roster, predicate<person> Tester)

When the Java runtime invokes printPersons a method, it expects a CheckPerson type of data, so the lambda expression is this type. When the Java runtime invokes printPersonsWithPredicate a method, it expects a Predicate&lt;Person&gt; type of data, so a lambda expression is a type. The data type expected by these methods is called the target type. To determine the type of the lambda expression, the Java compiler determines its target type in the context of the lambda expression. Only the Java compiler can infer the target type, and the lambda expression can be executed.

Target type and function parameters

For function arguments, the Java compiler can determine the target type through two other language features: overload resolution and type parameter inference. Look at the following two functional interfaces (Java.lang.Runnable and java.util.concurrent.callable<v>):

public interface Runnable {    void run();}public interface Callable<V> {    V call();}

Runnable.runthe method does not return any values, but Callable&lt;V&gt;.call has a return value. Suppose you overloaded the method as follows invoke :

void invoke(Runnable r) {    r.run();}<T> T invoke(Callable<T> c) {    return c.call();}

So which method will be called when executing the following program?

String s = invoke(() -> "done");

Method is invoke(Callable&lt;T&gt;) called because the method returns a value, and the method invoke(Runnable) does not return a value. In this case, () -&gt; "done" the type of the lambda expression is Callable&lt;T&gt; .

At last

Thanks for reading, interested in the public account to get the latest push articles.

Java 8 lambda expression One look at it.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.