JDK1.8新特性lambda表达式 官方文档

JDK1.8官方文档说明原址

匿名类的一个问题是,如果您的匿名类的实现非常简单,例如一个接口只包含一个方法,那么匿名类的语法可能看起来很笨拙和不清楚。在这些情况下,您通常尝试将功能作为参数传递给另一个方法,例如当有人单击按钮时应该采取什么操作。Lambda表达式允许您这样做,将功能视为方法参数,或将代码视为数据。
前一节,匿名类,向您展示了如何实现一个基类而不给它一个名称。尽管这通常比命名类更简洁,但对于只有一个方法的类,即使是匿名类也显得有些多余和麻烦。Lambda表达式允许您更紧凑地表达单一方法类的实例。

  1. Lambda表达式的理想用例

Suppose that you are creating a social networking application. You want to create a feature that enables an administrator to perform any kind of action, such as sending a message, on members of the social networking application that satisfy certain criteria. The following table describes this use case in detail
假设您正在创建社交网络应用程序,如果你想创建一个功能,使管理员能够执行任何类型的行动,如发送消息,满足社会网络应用程序成员的一定的标准。下面的表描述了这个用例细节:

Field Description
Name名字 Perform action on selected members对选定成员执行操作
Primary Actor 主要演员 Administrator管理员
Preconditions 前提条件 Administrator is logged in to the system.管理员登录到系统。
Postconditions 后置条件 Action is performed only on members that fit the specified criteria.仅对符合指定标准的成员执行操作。
Main Success Scenario 主成功场景 1. Administrator specifies criteria of members on which to perform a certain action.管理员指定执行某些动作的成员的标准。2.Administrator specifies an action to perform on those selected members.管理员指定要在选定成员上执行的操作。3.Administrator selects the Submit button.管理员选择提交按钮。4.The system finds all members that match the specified criteria.系统查找与指定标准匹配的所有成员。5.The system performs the specified action on all matching members.系统对所有匹配成员执行指定的操作。
Extensions 拓展 1a. Administrator has an option to preview those members who match the specified criteria before he or she specifies the action to be performed or before selecting the Submit button.管理员可以在指定要执行的操作或选择提交按钮之前预览与指定条件匹配的成员。
Frequency of Occurrence 出现频次 Many times during the day.一天会出现多次

Suppose that members of this social networking application are represented by the following Person class:
假设此社交网络应用程序的成员由以下人员类表示:


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

Suppose that the members of your social networking application are stored in a List instance.
  假设你的社交网络应用程序的成员存储在List实例中。
  This section begins with a naive approach to this use case. It improves upon this approach with local and anonymous classes, and then finishes with an efficient and concise approach using lambda expressions. Find the code excerpts described in this section in the example RosterTest.
  这部分我们首先使用一个幼稚的方法。它改善了使用当地和匿名类的方法,然后完成一个简洁的使用Lambda表达式的方法。我们可以在示例RosterTest中找到这部分描述的代码片段。

Approach方法 1: Create Methods That Search for Members That Match One Characteristic 创建查找匹配一个特性的成员的方法

One simplistic approach is to create several methods; each method searches for members that match one characteristic, such as gender or age. The following method prints members that are older than a specified age:
  一种简单化的方法是创建几种方法,每个方法搜索符合性别、年龄等特征的成员。以下方法打印大于指定年龄的成员:


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

Note: A List is an ordered Collection. A collection is an object that groups multiple elements into a single unit. Collections are used to store, retrieve, manipulate, and communicate aggregate data. For more information about collections, see theCollections trail.
注意:List 是有序集合。集合是将多个元素组合成单个单元的对象。集合用于存储、检索、操作和通信聚合数据。有关集合的更多信息,请参见Collections trail。
  This approach can potentially make your application brittle, which is the likelihood of an application not working because of the introduction of updates (such as newer data types). Suppose that you upgrade your application and change the structure of the Person class such that it contains different member variables; perhaps the class records and measures ages with a different data type or algorithm. You would have to rewrite a lot of your API to accommodate this change. In addition, this approach is unnecessarily restrictive; what if you wanted to print members younger than a certain age, for example?
  这个方法可能会使应用程序变得脆弱,存在应用程序无法工作的可能性,原因是应用程序的更新(例如更新的数据类型)。假设您升级了应用程序并更改了类的结构,使其包含不同的成员变量;你也将不得不重写很多API来适应这种变化。此外,这种方法包含了不必要的限制,例如:如果你想打印的成员比一定年龄的年轻?

Approach 2: Create More Generalized Search Methods 创建更广义的搜索方法

The following method is more generic than printPersonsOlderThan; it prints members within a specified range of ages:
下面的方法比printpersonsolderthan更通用的;它将打印指定范围内的成员的年龄:


public static void printPersonsWithinAgeRange(
    List<Person> roster, int low, int high) {
    for (Person p : roster) {
    	//查询年轻区间在low和high之间的data
        if (low <= p.getAge() && p.getAge() < high) {
            p.printPerson();
        }
    }
}

What if you want to print members of a specified sex, or a combination of a specified gender and age range? What if you decide to change the Person class and add other attributes such as relationship status or geographical location? Although this method is more generic than printPersonsOlderThan, trying to create a separate method for each possible search query can still lead to brittle code. You can instead separate the code that specifies the criteria for which you want to search in a different class.
  如果您想打印指定性别的成员,或指定性别和年龄范围的组合,该怎么办?如果您决定更改人物类并对其添加其他属性,如关系状态或地理位置,该怎么办?虽然这种方法比printpersonsolderthan更通用,努力创造一个单独的方法来实现每一个可能的搜索查询,然而仍然可能导致代码比较脆弱。你可以在不同的类中指定不同搜索条件,从而将其分隔开。

Approach 3: Specify Search Criteria Code in a Local Class 在内部类中指定搜索条件代码

The following method prints members that match search criteria that you specify:
下面的方法打印与指定的搜索条件相匹配的成员:


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

This method checks each Person instance contained in the List parameter roster whether it satisfies the search criteria specified in the CheckPerson parameter tester by invoking the method tester.test. If the method tester.test returns a true value, then the method printPersons is invoked on the Person instance.
这个方法会通过调用方法tester.test检查roster 中包含的每一个Person 实例是否满足tester 指定的搜索条件。如果tester.test方法返回true,printPersons 方法则会执行Person实例。

o specify the search criteria, you implement the CheckPerson interface:
为了指定搜索条件,你需要实现CheckPerson 接口:


interface CheckPerson {
    boolean test(Person p);
}

The following class implements the CheckPerson interface by specifying an implementation for the method test. This method filters members that are eligible for Selective Service in the United States: it returns a true value if its Person parameter is male and between the ages of 18 and 25:
  下面的这个类实现了CheckPerson 接口,并指定了test方法的具体实现。此方法筛选在美国有资格进行选择性服务的成员:如果其参数为男性,年龄在18岁至25岁之间,则返回真实值:

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

To use this class, you create a new instance of it and invoke the printPersons method:
使用这个类,你可以创建它的一个新的实例并调用printpersons方法:


printPersons(
    roster, new CheckPersonEligibleForSelectiveService());

Although this approach is less brittle—you don’t have to rewrite methods if you change the structure of the Person—you still have additional code: a new interface and a local class for each search you plan to perform in your application. Because CheckPersonEligibleForSelectiveService implements an interface, you can use an anonymous class instead of a local class and bypass the need to declare a new class for each search.
  尽管这种方法不那么脆弱,如果你改变了Person的结构你不必重写方法了,但是你还需要额外添加代码:你计划在你的应用程序中执行的新的接口和内部类。因为CheckPersonEligibleForSelectiveService 实现了一个接口,你可以使用一个匿名类而不是内部类并且绕过了为每一个搜索声明一个新类。

Approach 4: Specify Search Criteria Code in an Anonymous Class 在匿名类中指定搜索条件代码

One of the arguments of the following invocation of the method printPersons is an anonymous class that filters members that are eligible for Selective Service in the United States: those who are male and between the ages of 18 and 25:
  printPersons 方法中包含一个只有一个参数的匿名函数,其作用是过滤在美国有选择服务资格的人的条件:男性以及年龄在18岁至25岁之间:


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

This approach reduces the amount of code required because you don’t have to create a new class for each search that you want to perform. However, the syntax of anonymous classes is bulky considering that the CheckPerson interface contains only one method. In this case, you can use a lambda expression instead of an anonymous class, as described in the next section.
  此方法减少了所需代码的数量,因为您不必为要执行的每个搜索创建新类。然而,匿名类的语法是考虑到checkperson接口只包含一个方法笨重。在这种情况下,你可以使用一个而不是一个匿名类的lambda表达式,如在下一节中描述。

Approach 5: Specify Search Criteria Code with a Lambda Expression使用lambda表达式指定搜索条件代码

The CheckPerson interface is a functional interface(函数式). A functional interface is any interface that contains only one abstract method. (A functional interface may contain one or more default methods or static methods.) Because a functional interface contains only one abstract method, you can omit the name of that method when you implement it. To do this, instead of using an anonymous class expression, you use a lambda expression, which is highlighted in the following method invocation:
CheckPerson 接口是一个函数式接口。一个函数式接口是只包含一个抽象方法的接口。(一个函数式接口中可以包含一个或多个默认方法或者静态方法)因为一个函数接口只包含一个抽象方法,所以当你实现它时,可以省略该方法的名称。要做到这一点,而不是使用一个匿名类的表达,你使用lambda表达式,由以下方法调用了:

//根据性别和年龄区间查询具体的某个人
printPersons(
    roster,
    (Person p) -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25
);

See Syntax of Lambda Expressions for information about how to define lambda expressions.可以查看lambda表达式的语法来了解如何定义lambda表达式。
  You can use a standard functional interface in place of the interface CheckPerson, which reduces even further the amount of code required.
  你可以在界面checkperson处使用一个标准的功能接口,从而进一步降低所需的代码。

Approach 6: Use Standard Functional Interfaces with Lambda Expressions 使用标准的功能接口与Lambda表达式

interface CheckPerson {
    boolean test(Person p);
}

The interface Predicate is an example of a generic interface. (For more information about generics, see the Generics (Updated) lesson.) Generic types (such as generic interfaces) specify one or more type parameters within angle brackets (<>). This interface contains only one type parameter, T. When you declare or instantiate a generic type with actual type arguments, you have a parameterized type. For example, the parameterized type Predicate is the following:  
接口Predicate是一个泛型接口。(更多关于泛型的内容,参见Generics (Updated))。泛型类型(如泛型接口)是在尖括号中指定一个或多个类型参数。这个接口中只包含一个类型参数T。当你使用一个实际类型参数声明或实例化一个类属性,你就有一个参数化的 类型。例如,参数化类型Predicate :

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

This parameterized type contains a method that has the same return type and parameters as CheckPerson.boolean test(Person p). Consequently, you can use Predicate in place of CheckPerson as the following method demonstrates:
  这种参数化的类型包含一个返回值类型以及参数与CheckPerson.boolean test(Person p)一样的方法。因此,你可以在使用Predicate替换掉CheckPerson ,就像下面方法演示一样:

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

As a result, the following method invocation is the same as when you invoked printPersons in Approach 3: Specify Search Criteria Code in a Local Class to obtain members who are eligible for Selective Service:
  因此,下面的方法调用与你调用Approach 3: Specify Search Criteria Code in a Local Class中的printPersons是一样,用来获得有选择服务资格的会员:

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

This is not the only possible place in this method to use a lambda
expression. The following approach suggests other ways to use lambda
expressions.

这并不是在该方法中唯一可能使用Lambda表达式的地方。以下方法举例了的其它方式使用lambda表达式。

Approach 7: Use Lambda Expressions Throughout Your Application在整个应用程序中使用Lambda表达式

Reconsider the method printPersonsWithPredicate to see where else you
could use lambda expressions:

重新考虑printPersonsWithPredicate看是否还有别的地方可以使用Lambda表达式:

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

This method checks each Person instance contained in the List
parameter roster whether it satisfies the criteria specified in the
Predicate parameter tester. If the Person instance does satisfy the
criteria specified by tester, the method printPersron is invoked on
the Person instance.

这个方法检查List类型参数roster 中的每个Person 实例是否满足Predicate
类型接口tester规定的标准。如果Person 满足tester中指定的测试标准,Person中的printPersron方法将会被执行。

Instead of invoking the method printPerson, you can specify a
different action to perform on those Person instances that satisfy the
criteria specified by tester. You can specify this action with a
lambda expression. Suppose you want a lambda expression similar to
printPerson, one that takes one argument (an object of type Person)
and returns void. Remember, to use a lambda expression, you need to
implement a functional interface. In this case, you need a functional
interface that contains an abstract method that can take one argument
of type Person and returns void. The Consumer interface contains
the method void accept(T t), which has these characteristics. The
following method replaces the invocation p.printPerson() with an
instance of Consumer that invokes the method accept:

替换printPerson方法,我们可以在Person实例中指定一个符合tester规定的标准的不同的要执行的操作。你可以用lambda表达式指定动作。加入你想要一个类似于printPerson的Lambda表达式,一个带有一个参数(Person类型对象)并且返回值为void。我们应当注意,在使用Lambda表达式,我们需要一个函数式接口。在这种情况下,你需要一个包含一个Person类型的参数并且返回值为void的抽象方法。Consumer接口包含方法void
accept(T t),满足需要的这些特征。下面我们使用Consumer来替换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);
            }
        }
}

As a result, the following method invocation is the same as when you
invoked printPersons in Approach 3: Specify Search Criteria Code in a
Local Class to obtain members who are eligible for Selective Service.
The lambda expression used to print members is highlighted:
  因此,下面的方法执行与当你调用并执行Approach 3: Specify Search Criteria Code in a
Local Class的printPersons 一样,获得有选择服务资格的会员。Lambda表达式用来打印成员:

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 your members’ profiles than
printing them out. Suppose that you want to validate the members’
profiles or retrieve their contact information? In this case, you need
a functional interface that contains an abstract method that returns a
value. The Function<T,R> interface contains the method R apply(T t).
The following method retrieves the data specified by the parameter
mapper, and then performs an action on it specified by the parameter
block:

如果你想在打印成员简介的时候打印更多内容该怎么办?假设你想要验证成员的简介的合法性或检索他们的联系方式该怎么办?在这种情况下,需要一个包含返回值的抽象方法的函数式接口。Function<T,R>接口包含方法R
apply(T t)。下面的方法通过参数mapper检索数据,然后通过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 method retrieves the email address from each member
contained in roster who is eligible for Selective Service and then
prints it:   下面的方法是从包含在可选服务中的成员中检索每个成员的电子邮件地址,然后打印它:

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

A

Approach 8: Use Generics More Extensively 更广泛地使用泛型

Reconsider the method processPersonsWithFunction. The following is a
generic version of it that accepts, as a parameter, a collection that
contains elements of any data type:

重新考虑processPersonsWithFunction方法。 下面的泛型示例,接收一个包含任何数据类型的集合作为参数。

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 the e-mail address of members who are eligible for Selective
Service, invoke the processElements method as follows:
  将打印有资格选择服务成员的电子邮件地址,调用processelements方法如下:

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

This method invocation performs the following actions此方法调用执行下列操作:

Obtains a source of objects from the collection source. In this
example, it obtains a source of Person objects from the collection
roster. Notice that the collection roster, which is a collection of
type List, is also an object of type Iterable. Filters objects that
match the Predicate object tester. In this example, the Predicate
object is a lambda expression that specifies which members would be
eligible for Selective Service. Maps each filtered object to a value
as specified by the Function object mapper. In this example, the
Function object is a lambda expression that returns the e-mail address
of a member. Performs an action on each mapped object as specified by
the Consumer object block. In this example, the Consumer object is a
lambda expression that prints a string, which is the e-mail address
returned by the Function object. You can replace each of these actions
with an aggregate operation.你可以使用聚合操作替换每个操作。

Approach 9: Use Aggregate Operations That Accept Lambda Expressions as Parameters

The following example uses aggregate operations to print the e-mail
addresses of those members contained in the collection roster who are
eligible for Selective Service:

下面的示例使用聚合操作打印包含可选服务的集合列表中包含的成员的电子邮件地址:

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 each of the operations the method
processElements performs with the corresponding aggregate operation:

processElements Action Aggregate Operation Obtain a source of
objects Stream stream() Filter objects that match a Predicate
object Stream filter(Predicate<? super T> predicate) Map objects to
another value as specified by a Function object Stream
map(Function<? super T,? extends R> mapper) Perform an action as
specified by a Consumer object void forEach(Consumer<? super T>
action)
  The operations filter, map, and forEach are aggregate operations. Aggregate operations process elements from a stream, not directly from
a collection (which is the reason why the first method invoked in this
example is stream). A stream is a sequence of elements. Unlike a
collection, it is not a data structure that stores elements. Instead,
a stream carries values from a source, such as collection, through a
pipeline. A pipeline is a sequence of stream operations, which in this
example is filter- map-forEach. In addition, aggregate operations
typically accept lambda expressions as parameters, enabling you to
customize how they behave.

For a more thorough discussion of aggregate operations, see the
Aggregate Operations lesson.

Lambda Expressions in GUI Applications   To process events in a
graphical user interface (GUI) application, such as keyboard actions,
mouse actions, and scroll actions, you typically create event
handlers, which usually involves implementing a particular interface.
Often, event handler interfaces are functional interfaces; they tend
to have only one method.

在GUI应用程序中处理事件,例如键盘操作,鼠标操作和滚轮操作,通常创建事件处理程序,通常关联实现特定接口。通常事件处理接口是函数式接口;他们往往只有一个方法。
  In the JavaFX example HelloWorld.java (discussed in the previous
section Anonymous Classes), you can replace the highlighted anonymous
class with a lambda expression in this statement:

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

The method invocation btn.setOnAction specifies what happens when you
select the button represented by the btn object. This method requires
an object of typeEventHandler. The
EventHandler interface contains only one method, void
handle(T event). This interface is a functional interface, so you
could use the following highlighted lambda expression to replace it:

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

Syntax(语法) of Lambda Expressions A lambda expression consists of the
following一个Lambda表达式包括以下内容:

A comma-separated(逗号分隔) list of formal parameters enclosed(封闭形式参数) in
parentheses(圆括号). The CheckPerson.test method contains one parameter,
p, which represents(代表) an instance(实例) of the Personclass.

Note: You can omit(省略) the data type of the parameters in a lambda
expression. In addition(此外), you can omit(省略) the parentheses(圆括号) if
there is only one parameter. For example, the following lambda
expression is also valid:

p -> p.getGender() == Person.Sex.MALE 
    && p.getAge() >= 18
    && p.getAge() <= 25
The arrow token, ->(箭头标记)

A body, which consists of a single expression or a statement block.
This example uses the following expression由单个表达式或语句块组成的主体。此示例使用以下表达式:

:

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

If you specify a single expression, then the Java runtime evaluates
the expression and then returns its value(大意:将表达式的结果作为返回值返回).
Alternatively(或者), you can use a return statement(声明):

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

A return statement is not an expression; in a lambda expression, you
must enclose(把…围起来) statements in braces ({}). However(然而), you do not
have to enclose a void method invocation in braces. For example, the
following is a valid(有效的) lambda expression:

email -> System.out.println(email)   Note that a lambda expression
looks a lot like a method declaration; you can consider lambda
expressions as anonymous methods—methods without a name.

注意,一个lambda表达式看起来像一个方法声明;你可以考虑lambda表达式作为匿名方法。   The following
example, Calculator, is an example of lambda expressions that take
more than one formal parameter:

下面的计算器例子中,lambda表达式接收多个形参:

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

The method operateBinary performs a mathematical operation(执行数学运算) on
two integer operands(操作数). The operation itself is specified by an
instance of IntegerMath. The example defines two operations with
lambda expressions, addition and subtraction. The example prints the
following:

40 + 2 = 42 20 - 10 = 10 Accessing Local Variables of the Enclosing
Scope 访问封闭范围的局部变量   Like local and anonymous classes, lambda
expressions can capture variables; they have the same access to local
variables of the enclosing scope. However, unlike local and anonymous
classes, lambda expressions do not have anyshadowing issues (see
Shadowing for more information). Lambda expressions are lexically
scoped. This means that they do not inherit any names from a supertype
or introducea new level of scoping. Declarations in a lambda
expression are interpreted just as they are in the enclosing
environment. The following example,LambdaScopeTest, demonstrates this:
  像本地和匿名函数,lambda表达式可以捕获变量;他们有权限访问封闭范围内的本地变量。然而,不同于本地和匿名类,lambda表达式没有任何跟踪问题
(see Shadowing for more
information)。Lambda表达式是词法作用域。这意味着他们没有继承任何名字从父类型或引入一个新的水平域。在一个lambda表达式声明解释就像是在封闭的环境。下面的例子中,LambdaScopeTest,演示了这一点:

import java.util.function.Consumer;
 
public class LambdaScopeTest {
 
    public int x = 0;
 
    class FirstLevel {
 
        public int x = 1;
 
        void methodInFirstLevel(int x) {
            
            // The following statement causes the compiler to generate
            // the error "local variables referenced from a lambda expression
            // must be final or effectively final" in statement A:
            //
            // x = 99;
            
            Consumer<Integer> myConsumer = (y) -> 
            {
                System.out.println("x = " + x); // Statement A
                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);
    }
}
This example generates the following output:

x = 23
y = 23
this.x = 1
LambdaScopeTest.this.x = 0
If you substitute(代替) the parameter x in place of y in the declaration of the lambda expression myConsumer, then the compiler generates an error:

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

The compiler generates the error “variable x is already defined in
method methodInFirstLevel(int)” because the lambda expression does not
introduce a new level of scoping. Consequently, you can directly
access fields, methods, and local variables of the enclosing scope.
For example, the lambda expression directly accesses the parameter x
of the methodmethodInFirstLevel. To access variables in the enclosing
class, use the keyword this. In this example, this.x refers to the
member variable FirstLevel.x.

However, like local and anonymous classes, a lambda expression can
only access local variables and parameters of the enclosing block that
are final or effectively final. For example, suppose that you add the
following assignment statement immediately after the
methodInFirstLevel definition statement:

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

Because of this assignment statement, the variable FirstLevel.x is not
effectively final anymore. As a result, the Java compiler generates an
error message similar to “local variables referenced from a lambda
expression must be final or effectively final” where the lambda
expression myConsumer tries to access the FirstLevel.x variable:

System.out.println("x = " + x);

Target Typing How do you determine the type of a lambda expression?
Recall the lambda expression that selected members who are male and
between the ages 18 and 25 years:

p -> p.getGender() == Person.Sex.MALE
    && p.getAge() >= 18
    && p.getAge() <= 25
> This lambda expression was used in the following two methods:
> 
> public static void printPersons(List<Person> roster, CheckPerson
> tester) in Approach 3: Specify Search Criteria Code in a Local Class
> 
> public void printPersonsWithPredicate(List<Person> roster,
> Predicate<Person> tester) in Approach 6: Use Standard Functional
> Interfaces with Lambda Expressions
> 
> When the Java runtime invokes the method printPersons, it's expecting
> a data type of CheckPerson, so the lambda expression is of this type.
> However, when the Java runtime invokes the method
> printPersonsWithPredicate, it's expecting a data type of
> Predicate<Person>, so the lambda expression is of this type. The data
> type that these methods expect is called the target type. To determine
> the type of a lambda expression, the Java compiler uses the target
> type of the context or situation in which the lambda expression was
> found. It follows that you can only use lambda expressions in
> situations in which the Java compiler can determine a target type:
> 
> Variable declarations
> 
> Assignments
> 
> Return statements
> 
> Array initializers
> 
> Method or constructor arguments
> 
> Lambda expression bodies
> 
> Conditional expressions, ?:
> 
> Cast expressions
> 
> Target Types and Method Arguments For method arguments, the Java
> compiler determines the target type with two other language features:
> overload resolution and type argument inference.

Consider 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();
}
The method Runnable.run does not return a value, whereas Callable<V>.call does.

Suppose that you have overloaded the method invoke as follows (see Defining Methods for more information about overloading methods):

void invoke(Runnable r) {
    r.run();
}
 
<T> T invoke(Callable<T> c) {
    return c.call();
}
Which method will be invoked in the following statement?

String s = invoke(() -> "done");
The method invoke(Callable<T>) will be invoked because that method returns a value; the method invoke(Runnable) does not. In this case, the type of the lambda expression () -> "done" is Callable<T>.

Serialization
You can serialize a lambda expression if its target type and its captured arguments are serializable. However, like inner classes, the serialization of lambda expressions is strongly discouraged.

```java
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.Comparator;
import java.util.function.Predicate;
import java.lang.Iterable;
import java.time.chrono.IsoChronology;
 
public class RosterTest {
 
    interface CheckPerson {
        boolean test(Person p);
    }
 
    // Approach 1: Create Methods that Search for Persons that Match One
    // Characteristic
 
    public static void printPersonsOlderThan(List<Person> roster, int age) {
        for (Person p : roster) {
            if (p.getAge() >= age) {
                p.printPerson();
            }
        }
    }
// Approach 2: Create More Generalized Search Methods
 
public static void printPersonsWithinAgeRange(
    List<Person> roster, int low, int high) {
    for (Person p : roster) {
        if (low <= p.getAge() && p.getAge() < high) {
            p.printPerson();
        }
    }
}
 

// Approach 3: Specify Search Criteria Code in a Local Class
// Approach 4: Specify Search Criteria Code in an Anonymous Class
// Approach 5: Specify Search Criteria Code with a Lambda Expression

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

Approach 6: Use Standard Functional Interfaces with Lambda Expressions对Lambda表达式使用标准函数接口

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

// Approach 7: Use Lambda Expressions Throughout Your Application在整个应用程序中使用Lambda表达式

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

Approach 7, second example 第二个例子

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

Approach 8: Use Generics More Extensively更广泛地使用泛型

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);
            }
        }
}
 
public static void main(String... args) {
 
    List<Person> roster = Person.createRoster();
 
    for (Person p : roster) {
        p.printPerson();
    }
 
      // Approach 1: Create Methods that Search for Persons that Match One
        // Characteristic
 
        System.out.println("Persons older than 20:");
        printPersonsOlderThan(roster, 20);
        System.out.println();
 
        // Approach 2: Create More Generalized Search Methods
 
        System.out.println("Persons between the ages of 14 and 30:");
        printPersonsWithinAgeRange(roster, 14, 30);
        System.out.println();
 
        // Approach 3: Specify Search Criteria Code in a Local Class
 
        System.out.println("Persons who are eligible for Selective Service:");
 
        class CheckPersonEligibleForSelectiveService implements CheckPerson {
           public boolean test(Person p) {
                return p.getGender() == Person.Sex.MALE
                    && p.getAge() >= 18
                    && p.getAge() <= 25;
            }
        }
 
        printPersons(
            roster, new CheckPersonEligibleForSelectiveService());
 
 
        System.out.println();
 
        // Approach 4: Specify Search Criteria Code in an Anonymous Class
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(anonymous class):");
 
        printPersons(
            roster,
            new CheckPerson() {
                public boolean test(Person p) {
                    return p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25;
                }
            }
        );
 
        System.out.println();
 
        // Approach 5: Specify Search Criteria Code with a Lambda Expression
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(lambda expression):");
 
        printPersons(
            roster,
            (Person p) -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25
        );
 
        System.out.println();
 
        // Approach 6: Use Standard Functional Interfaces with Lambda
        // Expressions
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(with Predicate parameter):");
 
        printPersonsWithPredicate(
            roster,
            p -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25
        );
 
        System.out.println();
 
        // Approach 7: Use Lamba Expressions Throughout Your Application
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(with Predicate and Consumer parameters):");
 
        processPersons(
            roster,
            p -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25,
            p -> p.printPerson()
        );
 
        System.out.println();
 
        // Approach 7, second example
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(with Predicate, Function, and Consumer parameters):");
 
        processPersonsWithFunction(
            roster,
            p -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25,
            p -> p.getEmailAddress(),
            email -> System.out.println(email)
        );
 
        System.out.println();
 
        // Approach 8: Use Generics More Extensively
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(generic version):");
 
        processElements(
            roster,
            p -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25,
            p -> p.getEmailAddress(),
            email -> System.out.println(email)
        );
 
        System.out.println();
 
        // Approach 9: Use Bulk Data Operations That Accept Lambda Expressions
        // as Parameters
 
        System.out.println("Persons who are eligible for Selective Service " +
            "(with bulk data operations):");
 
        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));
     }
}

猜你喜欢

转载自blog.csdn.net/qq_43565087/article/details/106057842