The Java Tutorials have been written for JDK 8.Java教程是为JDK 8编写的。Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available.本页中描述的示例和实践没有利用后续版本中引入的改进,并且可能使用不再可用的技术。See Java Language Changes for a summary of updated language features in Java SE 9 and subsequent releases.有关Java SE 9及其后续版本中更新的语言特性的摘要,请参阅Java语言更改。
See JDK Release Notes for information about new features, enhancements, and removed or deprecated options for all JDK releases.有关所有JDK版本的新功能、增强功能以及已删除或不推荐的选项的信息,请参阅JDK发行说明。
One issue with anonymous classes is that if the implementation of your anonymous class is very simple, such as an interface that contains only one method, then the syntax of anonymous classes may seem unwieldy and unclear.匿名类的一个问题是,如果匿名类的实现非常简单,例如只包含一个方法的接口,那么匿名类的语法可能看起来很难理解。In these cases, you're usually trying to pass functionality as an argument to another method, such as what action should be taken when someone clicks a button.在这些情况下,您通常试图将功能作为参数传递给另一个方法,例如当有人单击按钮时应该采取什么操作。Lambda expressions enable you to do this, to treat functionality as method argument, or code as data.Lambda表达式允许您这样做,将功能视为方法参数,或将代码视为数据。
The previous section, Anonymous Classes, shows you how to implement a base class without giving it a name.上一节,匿名类向您展示了如何在不给基类命名的情况下实现它。Although this is often more concise than a named class, for classes with only one method, even an anonymous class seems a bit excessive and cumbersome.虽然这通常比命名类更简洁,但对于只有一个方法的类,即使是匿名类也似乎有点过分和麻烦。Lambda expressions let you express instances of single-method classes more compactly.Lambda表达式使您能够更简洁地表达单个方法类的实例。
This section covers the following topics:本节涵盖以下主题:
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:下表详细描述了此用例:
Name | |
Primary Actor | |
Preconditions | |
Postconditions | |
| |
1a. | |
Suppose that members of this social networking application are represented by the following 假设此社交网络应用程序的成员由以下Person
class:Person
类表示:
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<Person>
instance.List<Person>
实例中。
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.它使用本地类和匿名类改进了这种方法,然后使用lambda表达式以一种高效简洁的方法结束。Find the code excerpts described in this section in the example 在示例RosterTest
.RosterTest
中查找本节中描述的代码摘录。
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.:List是一个有序的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 the 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.Person
类的结构,使其包含不同的成员变量;也许类使用不同的数据类型或算法记录和测量年龄。You would have to rewrite a lot of your API to accommodate this change.您必须重写大量API以适应此更改。In addition, this approach is unnecessarily restrictive; what if you wanted to print members younger than a certain age, for example?此外,这种方法具有不必要的限制性;例如,如果要打印小于某个年龄的成员,该怎么办?
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) { 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?Person
类并添加其他属性,如关系状态或地理位置,该怎么办?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.printPersonsOlderThan
更通用,但尝试为每个可能的搜索查询创建单独的方法仍然会导致代码脆弱。You can instead separate the code that specifies the criteria for which you want to search in a different 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
.tester.test
方法检查List
参数roster
中包含的每个Person
实例是否满足CheckPerson
参数tester
中指定的搜索条件。If the method 如果方法tester.test
returns a true
value, then the method printPersons
is invoked on the Person
instance.tester.test
返回一个true
值,则在Person
实例上调用方法printPersons
。
To 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
.test
的实现来实现CheckPerson
接口。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:Person
参数为男性且年龄在18到25岁之间,则返回true
值:
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
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.CheckPersonEligibleForSelectiveService
实现了一个接口,所以您可以使用匿名类而不是本地类,并且不需要为每次搜索声明新类。
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.CheckPerson
接口只包含一个方法,匿名类的语法非常庞大。In this case, you can use a lambda expression instead of an anonymous class, as described in the next section.在这种情况下,可以使用lambda表达式而不是匿名类,如下一节所述。
The CheckPerson
interface is a functional interface.CheckPerson
接口是一个功能接口。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:为此,您可以使用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
,这将进一步减少所需的代码量。
Reconsider the 重新考虑CheckPerson
interface:CheckPerson
接口:
interface CheckPerson { boolean test(Person p); }
This is a very simple interface. It's a functional interface because it contains only one abstract method.这是一个非常简单的界面。它是一个函数接口,因为它只包含一个抽象方法。This method takes one parameter and returns a 此方法接受一个参数并返回一个boolean
value.boolean
值。The method is so simple that it might not be worth it to define one in your application.该方法非常简单,可能不值得在应用程序中定义一个。Consequently, the JDK defines several standard functional interfaces, which you can find in the package 因此,JDK定义了几个标准的函数接口,您可以在包java.util.function
.java.util.function
中找到这些接口。
For example, you can use the 例如,您可以使用Predicate<T>
interface in place of CheckPerson
.Predicate<T>
接口代替CheckPerson
。This interface contains the method 此接口包含boolean test(T t)
:boolean test(T t)
方法:
interface Predicate<T> { boolean test(T t); }
The interface 接口Predicate<T>
is an example of a generic interface.Predicate<T>
是通用接口的一个示例。(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
.T
。When you declare or instantiate a generic type with actual type arguments, you have a parameterized type.当您使用实际的类型参数声明或实例化泛型类型时,您拥有一个参数化类型。For example, the parameterized type 例如,参数化类型Predicate<Person>
is the following:Predicate<Person>
详情如下:
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)
.CheckPerson.boolean test(Person p)
具有相同返回类型和参数的方法。Consequently, you can use 因此,您可以使用Predicate<T>
in place of CheckPerson
as the following method demonstrates:Predicate<T>
代替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 因此,以下方法调用与在方法3:在本地类中指定搜索条件代码中调用printPersons
in Approach 3: Specify Search Criteria Code in a Local Class to obtain members who are eligible for Selective Service: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.这不是此方法中使用lambda表达式的唯一可能位置。The following approach suggests other ways to use lambda expressions.下面的方法建议使用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
.List
参数roster
中包含的每个Person
实例是否满足Predicate
参数tester
中指定的条件。If the 如果Person
instance does satisfy the criteria specified by tester
, the method printPerson
is invoked on the Person
instance.Person
实例确实满足tester
指定的条件,则在Person
实例上调用printPerson
方法。
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
.tester
指定的条件的Person
实例执行,而不是调用printPerson
方法。You can specify this action with a lambda expression.可以使用lambda表达式指定此操作。Suppose you want a lambda expression similar to 假设您想要一个类似于printPerson
, one that takes one argument (an object of type Person
) and returns void.printPerson
的lambda表达式,该表达式接受一个参数(Person
类型的对象)并返回void
。Remember, to use a lambda expression, you need to implement a functional interface.记住,要使用lambda表达式,需要实现一个函数接口。In this case, you need a functional interface that contains an abstract method that can take one argument of type 在本例中,您需要一个函数接口,该接口包含一个抽象方法,该方法可以接受Person
and returns void.Person
类型的一个参数并返回void
。The Consumer<T>
interface contains the method void accept(T t)
, which has these characteristics.Consumer<T>
接口包含具有以下特征的void accept(T t)
方法。The following method replaces the invocation 以下方法将调用p.printPerson()
with an instance of Consumer<Person>
that invokes the method accept
:p.printPerson()
替换为调用accept
方法的Consumer<Person>
的实例:
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 因此,以下方法调用与在方法3:在局部类中指定搜索条件代码以获取符合选择性服务条件的成员中调用printPersons
in Approach 3: Specify Search Criteria Code in a Local Class to obtain members who are eligible for Selective Service.PrintPerson
时相同。The lambda expression used to print members is highlighted:用于打印成员的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)
.Function<T,R>
接口包含方法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
:mapper
指定的数据,然后对参数块指定的数据执行操作:
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:roster
中包含的每个有资格获得选择性服务的成员处检索电子邮件地址,然后打印该地址:
processPersonsWithFunction( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25, p -> p.getEmailAddress(), email -> System.out.println(email) );
Reconsider the method 重新考虑processPersonsWithFunction
.processPersonsWithFunction
方法。The following is a generic version of it that accepts, as a parameter, a collection that contains elements of 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); } } }
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:此方法调用执行以下操作:
source
.source
获取对象的源。Person
objects from the collection roster
.roster
中获取Person
对象的源。roster
, which is a collection of type List
, is also an object of type Iterable
.roster
是List
类型的集合,也是Iterable
类型的对象。Predicate
object tester
.Predicate
对象tester
匹配的对象。Predicate
object is a lambda expression that specifies which members would be eligible for Selective Service.Predicate
对象是一个lambda表达式,指定哪些成员有资格获得选择性服务。Function
object mapper
.Function
对象mapper
指定的值。Function
object is a lambda expression that returns the e-mail address of a member.Function
对象是一个lambda表达式,返回成员的电子邮件地址。Consumer
object block
.Consumer
对象block
的指定,对每个映射对象执行操作。Consumer
object is a lambda expression that prints a string, which is the e-mail address returned by the Function
object.Consumer
对象是一个lambda表达式,它打印一个字符串,该字符串是Function
对象返回的电子邮件地址。You can replace each of these actions with an aggregate operation.您可以用聚合操作替换这些操作中的每一个。
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
中包含的成员的电子邮件地址:
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
方法执行的每个操作与相应的聚合操作:
processElements |
|
---|---|
Stream<E> stream() | |
Predicate objectPredicate 对象匹配的对象 |
Stream<T> filter(Predicate<? super T> predicate) |
Function objectFunction 对象指定的另一个值 |
<R> Stream<R> map(Function<? super T,? extends R> mapper) |
Consumer objectConsumer 对象指定的操作 |
void forEach(Consumer<? super T> action) |
The operations 操作filter
, map
, and forEach
are aggregate operations.filter
、map
和forEach
是聚合操作。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
).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
.filter
-map
-forEach
。In addition, aggregate operations typically accept lambda expressions as parameters, enabling you to customize how they behave.此外,聚合操作通常接受lambda表达式作为参数,使您能够自定义它们的行为方式。
For a more thorough discussion of aggregate operations, see the Aggregate Operations lesson.有关聚合操作的更深入的讨论,请参阅聚合操作课程。
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.要处理图形用户界面(GUI)应用程序中的事件,例如键盘动作、鼠标动作和滚动动作,通常需要创建事件处理程序,这通常涉及实现特定的界面。Often, event handler interfaces are functional interfaces; they tend to have only one method.通常,事件处理程序接口是功能接口;他们往往只有一种方法。
In the JavaFX example 在JavaFX示例HelloWorld.java
(discussed in the previous section Anonymous Classes), you can replace the highlighted anonymous class with a lambda expression in this statement:HelloWorld.java
(在前面的匿名类部分中讨论)中,您可以使用以下语句中的lambda表达式替换高亮显示的匿名类:
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.btn.setOnAction
指定当您选择由btn
对象表示的按钮时会发生什么。This method requires an object of type 此方法需要EventHandler<ActionEvent>
.EventHandler<ActionEvent>
类型的对象。The EventHandler<ActionEvent>
interface contains only one method, void handle(T event)
.EventHandler<ActionEvent>
接口只包含一个方法,void handle(T event)
。This interface is a functional interface, so you could use the following highlighted lambda expression to replace it:此接口是一个函数接口,因此可以使用以下高亮显示的lambda表达式替换它:
btn.setOnAction(event -> System.out.println("Hello World!"));
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 Person
class.CheckPerson.test
方法包含一个参数p
,它表示Person
类的一个实例。
Note: You can omit the data type of the parameters in a lambda expression.注意:可以省略lambda表达式中参数的数据类型。In addition, you can omit the parentheses if there is only one parameter.此外,如果只有一个参数,则可以省略括号。For example, the following lambda expression is also valid:例如,以下lambda表达式也有效:
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.如果指定一个表达式,Java运行时将对该表达式求值,然后返回其值。Alternatively, you can use a return statement:或者,您可以使用return
语句:
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 (return语句不是表达式;在lambda表达式中,必须将语句括在大括号({}
).{}
)中。However, you do not have to enclose a void method invocation in braces.但是,不必将void方法调用括在大括号中。For example, the following is a valid lambda expression:例如,以下是有效的lambda表达式:
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:Calculator
是采用多个形式参数的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.operateBinary
方法对两个整数操作数执行数学运算。The operation itself is specified by an instance of 操作本身由IntegerMath
.IntegerMath
的实例指定。The example defines two operations with lambda expressions, 该示例使用lambda表达式定义了两个操作:addition
and subtraction
.addition
和subtraction
。The example prints the following:该示例打印以下内容:
40 + 2 = 42 20 - 10 = 10
Like local and anonymous classes, lambda expressions can capture variables; they have the same access to local variables of the enclosing scope.与本地类和匿名类一样,lambda表达式可以捕获变量;它们对封闭范围的局部变量具有相同的访问权限。However, unlike local and anonymous classes, lambda expressions do not have any shadowing issues (see Shadowing for more information).但是,与本地类和匿名类不同,lambda表达式没有任何阴影问题(有关更多信息,请参阅阴影)。Lambda expressions are lexically scoped.Lambda表达式在词汇范围内。This means that they do not inherit any names from a supertype or introduce a new level of scoping.这意味着它们不会从超类型继承任何名称,也不会引入新级别的作用域。Declarations in a lambda expression are interpreted just as they are in the enclosing environment.lambda表达式中的声明与封闭环境中的声明一样进行解释。The following example, 以下示例LambdaScopeTest
, demonstrates this:LambdaScopeTest
演示了这一点:
import java.util.function.Consumer; public class LambdaScopeTest { public int x = 0; class FirstLevel { public int x = 1; void methodInFirstLevel(int x) { int z = 2; Consumer<Integer> myConsumer = (y) -> { // The following statement causes the compiler to generate // the error "Local variable z defined in an enclosing scope // must be final or effectively final" // // z = 99; System.out.println("x = " + x); System.out.println("y = " + y); System.out.println("z = " + z); 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 z = 2 this.x = 1 LambdaScopeTest.this.x = 0
If you substitute the parameter 如果在lambda表达式x
in place of y
in the declaration of the lambda expression myConsumer
, then the compiler generates an error:myConsumer
的声明中用参数x
代替y
,则编译器将生成错误:
Consumer<Integer> myConsumer = (x) -> { // ... }
The compiler generates the error "Lambda expression's parameter x cannot redeclare another local variable defined in an enclosing scope" because the lambda expression does not introduce a new level of scoping.编译器生成错误“Lambda表达式的参数x无法重新声明封闭范围中定义的另一个局部变量”,因为Lambda表达式未引入新级别的作用域。Consequently, you can directly access fields, methods, and local variables of the enclosing scope.因此,您可以直接访问封闭范围的字段、方法和局部变量。For example, the lambda expression directly accesses the parameter 例如,lambda表达式直接访问x
of the method methodInFirstLevel
.methodInFirstLevel
方法的参数x
。To access variables in the enclosing class, use the keyword 要访问封闭类中的变量,请使用关键字this
.this
。In this example, 在本例中,this.x
refers to the member variable FirstLevel.x
.this.x
引用成员变量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.但是,与局部类和匿名类一样,lambda表达式只能访问封闭块的局部变量和参数,这些变量和参数是最终的或实际上是最终的。In this example, the variable 在这个例子中,变量z
is effectively final; its value is never changed after it's initialized.z
实际上是最终的;它的值在初始化后不会更改。However, suppose that you add the following assignment statement in the the lambda expression 但是,假设在lambda表达式myConsumer
:myConsumer
中添加以下赋值语句:
Consumer<Integer> myConsumer = (y) -> { z = 99; // ... }
Because of this assignment statement, the variable 由于这个赋值语句,变量z
is not effectively final anymore.z
实际上不再是最终。As a result, the Java compiler generates an error message similar to "Local variable z defined in an enclosing scope must be final or effectively final".结果,Java编译器生成类似于“在封闭范围内定义的局部变量z必须是final或实际上是final”的错误消息。
How do you determine the type of a lambda expression?如何确定lambda表达式的类型?Recall the lambda expression that selected members who are male and between the ages 18 and 25 years:回忆一下lambda的表达方式,即选择18至25岁的男性成员:
p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
This lambda expression was used in the following two methods:此lambda表达式用于以下两种方法:
public static void printPersons(List<Person> roster, CheckPerson tester)
in Approach 3: Specify Search Criteria Code in a Local Class:在方法3:在局部类中指定搜索条件代码中讲解。
public void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester)
in Approach 6: Use Standard Functional Interfaces with Lambda Expressions在方法6:对Lambda表达式使用标准函数接口中讲解。
When the Java runtime invokes the method 当Java运行时调用printPersons
, it's expecting a data type of CheckPerson
, so the lambda expression is of this type.printPerson
方法时,它希望数据类型为CheckPerson
,因此lambda表达式属于这种类型。However, when the Java runtime invokes the method 但是,当Java运行时调用printPersonsWithPredicate
, it's expecting a data type of Predicate<Person>
, so the lambda expression is of this type.printPersonsWithPredicate
方法时,它需要的数据类型是Predicate<Person>
,所以lambda表达式就是这种类型的。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.为了确定lambda表达式的类型,Java编译器使用找到lambda表达式的上下文或情况的目标类型。It follows that you can only use lambda expressions in situations in which the Java compiler can determine a target type:因此,您只能在Java编译器可以确定目标类型的情况下使用lambda表达式:
Variable declarations变量声明
Assignments赋值
Return statements返回语句
Array initializers数组初始值设定项
Method or constructor arguments方法或构造函数参数
Lambda expression bodiesLambda表达体
Conditional expressions,条件表达式,?:
Cast expressionsCast表达式
For method arguments, the Java compiler determines the target type with two other language features: overload resolution and type argument inference.对于方法参数,Java编译器使用另外两个语言特性确定目标类型:重载解析和类型参数推断。
Consider the following two functional interfaces (java.lang.Runnable and java.util.concurrent.Callable<V>):考虑下面两个函数接口(java.lang.Runnable和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.Runnable.run
不返回值,而Callable<V>.call
返回值。
Suppose that you have overloaded the method 假设您已按如下方式重载了invoke
as follows (see Defining Methods for more information about overloading methods):invoke
方法(有关重载方法的更多信息,请参阅定义方法):
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.invoke(Callable<T>)
,因为该方法返回一个值;而方法invoke(Runnable)
不返回值。In this case, the type of the lambda expression 在本例中,lambda表达式() -> "done"
is Callable<T>
.() -> "done"
是Callable<T>
。
You can serialize a lambda expression if its target type and its captured arguments are serializable.如果lambda表达式的目标类型及其捕获的参数可序列化,则可以序列化该表达式。However, like inner classes, the serialization of lambda expressions is strongly discouraged.但是,与内部类一样,强烈反对lambda表达式的序列化。