Java8
Lambda Expressions
目录
典型使用场景:Ideal Use Case for Lambda Expressions
问题提出:Approach 1: Create Methods That Search for Members That Match One Characteristic
更通用的方法:Approach 2: Create More Generalized Search Methods
封装:Approach 3: Specify Search Criteria Code in a Local Class
使用匿名内部类:Approach 4: Specify Search Criteria Code in an Anonymous Class
Lambda表达式:Approach 5: Specify Search Criteria Code with a Lambda Expression
功能接口补充:Approach 补充: Standard Functional Interfaces补充资料
Lambda结合功能接口:Approach 6: Use Standard Functional Interfaces with Lambda Expressions
Lambda应用场景:Approach 7: Use Lambda Expressions Throughout Your Application
Lambda更通用:Approach 8: Use Generics More Extensively
Lambda与聚合操作:Approach 9: Use Aggregate Operations That Accept Lambda Expressions as Parameters
GUI中使用Lambda:Lambda Expressions in GUI Applications(略)
Lambda语法:Syntax of Lambda Expressions
作用域:Accessing Local Variables of the Enclosing Scope
目标类型:Target Typing
目标类型与方法参数:Target Types and Method Arguments
序列化:Serialization
Lambda expressions enable you to do this, to treat functionality as method argument, or code as data.
λ(辣嗯哒)表达式提供了,函数闭包参数,和代码块数据定义,两种功能。
Lambda expressions let you express instances of single-method classes more compactly.
λ 表达式让你以更加优雅的方式 实例化一个单一功能的匿名内部类。
Ideal Use Case for Lambda Expressions
Suppose that you are creating a social networking application…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 |
|
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. |
这个假设是一个网站的用例图:
属性 | 描述 |
用例名 | 选择网站注册会员功能 |
参与者 | Administrator |
前置 | Administrator 已经登陆 |
后置 | 只有符合条件的会员才执行操作 |
主流程 |
|
扩展流程 | 1a. Administrator 可以选择预览所有符合条件的会员列表,在点击执行或者选择操作之前 |
频率 | 次/天 |
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() {
// ...
}
}
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 the Collections trail.
List是一个有序集合,(排列n! 可重复 n!/n1! n2!...nr!),更多信息自个去看 Collections。
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?
这种方式可能会使您的应用程序变得脆弱,这是由于引入变化(例如更新的数据类型,编码原则一:encapsulation what varies)而导致应用程序不工作。假设您的应用程序升级了并更改Person类的结构,使其包含不同的成员变量;也许类记录和度量使用不同的数据类型或算法。您将不得不重写许多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:
以下的方法更加的通用,使用一个比较范围。
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? 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.
尽管这个方法更加的通用了,试图为每个可能的搜索查询创建一个单独的方法仍然会导致脆弱的代码。编码原则一:encapsulation what varies,方法写得更加通用但是依然没有遵循编码原则:你可以将指定你想要在不同类中搜索的标准的代码分开。
Approach 3: Specify Search Criteria Code in a Local Class
The following method prints members that match search criteria that you specify:
下面的方法打印匹配您指定的搜索条件的会员:
// 在函数的参数中注入了依赖的 CheckPerson 类,
// CheckPerson 封装了搜索的标准
// method tester.test returns a true value, then the method //printPersons is invoked on the Person instance
// 方法 tester.test 返回 true 时,Person类printPersons 方法被执行
public static void printPersons(
List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
// To specify the search criteria, you implement the CheckPerson //interface:// 指定搜索条件,的CheckPerson接口:
interface CheckPerson { boolean test(Person p);}
// implements 其中一个实现
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(
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:
* 下面对方法printperson的调用的一个参数是一个匿名类,
* 它过滤了会员:那些是男性的,年龄在18到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表达式代替匿名类,如下一节所述。
*/
printPersons(
roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);
Approach 5: Specify Search Criteria Code with a Lambda Expression
/**
* 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.)
* CheckPerson 接口是一个功能接口(功能接口是指只有一个抽象方法的接口)
* 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
);
您可以使用标准的功能接口来代替接口CheckPerson
,这将进一步减少所需的代码量。You can use a standard functional interface in place of the interface CheckPerson
, which reduces even further the amount of code required.
Approach 补充: Standard Functional Interfaces补充资料
补充资料:
Let us 先看看Java8新增的几个标准功能接口是什么东东。java.util.function包。
1、接口已经通过@FunctionalInterface注解扩展为支持Lambda表达式。
2、在 Java 中,Marker(标记)类型的接口是一种没有方法或属性声明的接口,简单地说,marker 接口是空接口。相似地,函数式接口是只包含一个抽象方法声明的接口。
3、每个 Lambda 表达式都能隐式地赋值给函数式接口,当不指明函数式接口时,编译器会自动解释这种转化。
4、 断言接口(Predicates) 是只拥有一个参数的Boolean型功能的接口、
功能接口(Functions)Functions接受一个参数并产生一个结果、
供应接口(Supplier) 对于给定的泛型类型产生一个实例、
消费接口(Consumers)代表在只有一个输入参数时操作被如何执行、
比较接口(Comparators)
选项接口(Optionals)一种特殊的工具用来解决NullPointerException
注:参考:https://blog.csdn.net/u011659172/article/details/52584196
这些功能接口都是通过@FunctionalInterface注解标识,如:
/**
* Represents a supplier of results. 代表一个结果供应商
*
* <p>There is no requirement that a new or distinct result be returned each
* time the supplier is invoked.
* 不要求调用时一定要返回一个新的或不同的结果。
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #get()}.参考functional interface
*
* @param <T> the type of results supplied by this supplier
* 该Supplier提供的泛型结果类型
*
* @since 1.8
*/
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
以下是FunctionalInterface的源码及注释翻译:
/**
* An informative annotation type used to indicate that an interface
* type declaration is intended to be a <i>functional interface</i> as
* defined by the Java Language Specification.
* 1、一种注解类型。2、用于标识一个接口。3、用于声明一个标记接口,由Java语言规范定义的标记接口。
*
* Conceptually, a functional interface has exactly one abstract
* method. Since {@linkplain java.lang.reflect.Method#isDefault()
* default methods} have an implementation, they are not abstract. If
* an interface declares an abstract method overriding one of the
* public methods of {@code java.lang.Object}, that also does
* <em>not</em> count toward the interface's abstract method count
* since any implementation of the interface will have an
* implementation from {@code java.lang.Object} or elsewhere.
*
* 1、从概念上讲,功能接口只有一个抽象方法。默认方法有一个实现,它们不是抽象的。
* 2、如果一个接口声明一个抽象的方法,覆盖Object的公共方法,则这个抽象方法不计* 入的抽象方法数量因为这个接口的实现 能从Object或其他地方找到。
*
* <p>Note that instances of functional interfaces can be created with
* lambda expressions, method references, or constructor references.
*
* 请注意,功能性接口可以使用lambda表达式、方法引用或构造函数实例化。
*
* <p>If a type is annotated with this annotation type, compilers are
* required to generate an error message unless:
*
* <ul>
* <li> The type is an interface type and not an annotation type, enum, or class.
* <li> The annotated type satisfies the requirements of a functional interface.
* </ul>
*
* 如果一个枚举或者类被标注为FunctionalInterface,那么编译器将会报错,
* 除非:这个类型是一个接口,或者满足成为“标记接口”注解类型
*
* <p>However, the compiler will treat any interface meeting the
* definition of a functional interface as a functional interface
* regardless of whether or not a {@code FunctionalInterface}
* annotation is present on the interface declaration.
*
* 但是,只要存在FunctionalInterface编译器将把任何接口当标记接口处理,
* 不管是不是标记接口
*
*/
@Documented // 这个注解应该被 javadoc工具记录
@Retention(RetentionPolicy.RUNTIME) // 能在运行时被JVM或其他使用反射机制的代码所读取和使用
@Target(ElementType.TYPE) // 注解加载在类上
public @interface FunctionalInterface {}
Approach 6: Use Standard Functional Interfaces with Lambda Expressions
Reconsider the CheckPerson
interface:
重新考虑CheckPerson
接口:
interface CheckPerson {
boolean test(Person p);
}
这是一个非常简单的接口。它是一个功能接口,因为它只包含一个抽象方法。这个方法接受一个参数并返回一个布尔值。这个方法简单到,不值得在您的应用程序中定义这个接口。因此,JDK定义了几个标准的功能接口,您可以在java.util.function中找到它。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. 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 java.util.function
.
For example, you can use the Predicate<T>
interface in place of CheckPerson
. This interface contains the method boolean test(T t)
:
例如,您可以使用如下的公用Predicate接口来代替CheckPerson:
// This interface contains only one type parameter, T.
// 这个接口包含一个泛型参数 T
interface Predicate<T> {
boolean test(T t);
}
// When you declare or instantiate a generic type with actual type arguments
// 你可以定义或者实例化你实际需要的类型如Person
interface Predicate<Person> {
boolean test(Person t);
}
// Consequently, you can use Predicate<T> in place of CheckPerson as the following method demonstrates:
// 因此,您可以使用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:
// 因此,下列方法调用与在方法3中调用printperson时相同:
printPersonsWithPredicate(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
在这个方法中,这不是唯一可能使用lambda表达式的地方。下面的方法给出了使用lambda表达式的其他方法。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.
Approach 7: Use Lambda Expressions Throughout Your Application
Reconsider the method printPersonsWithPredicate
to see where else you could use lambda expressions:
重新考虑方法printPersonsWithPredicate
,看看您还可以在那些地方使用lambda表达式:
/**
* 检查roster列表中Person会员是否满足指定的Predicate,
* 如果满足则调用printPerson方法
*
*/
public static void printPersonsWithPredicate(
List<Person> roster, Predicate<Person> tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.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.
* 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<T> 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<Person> that invokes the method accept:
* 相比于调用printPersion方法,
* 你可以指定一个其他的方式来执行这些符合要求的Person实例,比如lambda表达式
* 假设你想要使用lambda表达式实现类似printPerson的功能
* 一个一个参数空返回值的pringPerson功能
* 记住使用lambda表达式,你需要实现一个功能函数
* 在本例种,您需要一个功能接口,他的抽象方法可以接受类型Person的参数并返回void。
* Consumer<T>包含了方法 void accept(T t),它具有这些特征。以下是实现:
*
*/
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
* 下面的方法调用与您在方法3中调用printperson时相同
*
*/
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:
* 如果我们想对会员的基本信息做更多的事情呢?
* 假设你想要验证会员的基本信息或者检索他们的联系方式?
* 在本例中,你需要一个 functional interface 包含了这样的抽象方法,返回一个值
* The Function<T,R> 接口包含了这个方法 R apply(T t).
* 下面的方法检索参数中指定的数据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:
// 下面的方法检索每个符合要求的会员的电子邮件地址,然后打印出来:
processPersonsWithFunction(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25,
p -> p.getEmailAddress(),
email -> System.out.println(email)
);
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方法。下面是它的一个更加通用版本,它接受作为一个参数的集合,它包含任何数据类型的元素
// is a generic version of it that accepts,
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
processElements(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25,
p -> p.getEmailAddress(),
email -> System.out.println(email)
);
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.This method invocation performs the following actions:
- 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.
这种方法调用执行下列操作流程:
- 从源集合中获得source对象,在本例中从roster列表中获取Person对象,注意roster是一个List集合,实现了Iterable迭代接口。
- 使用Predicate接口的tester方法过滤这些对象,在本例中,是Predicate对象中的lambda表达式负责。
- 映射符合要求的的对象到指定的Function对象的mapper中,在本例中,Function对象是一个lambda表达式,返回会员的email。
- 执行mapper中对象的的动作,由Consumer对象。在本例Consumer对象是一个lambda表达式,负责打印一个字符串,具体的就是Function对象返回的email。
You can replace each of these actions with an aggregate operation.
你可以用一个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:
下面的例子使用aggregate operations来打印包含在集合名册中的符合条件会员的电子邮件地址。
// 笨宝宝表示这个看不懂,看来aggregate 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));
processElements Action | Aggregate Operation |
Obtain a source of objects | Stream<E> stream() |
Filter objects that match a Predicate object | Stream<T> filter(Predicate<? super T> predicate) |
Map objects to another value as specified by a Function object | <R> Stream<R> map(Function<? super T,? extends R> mapper) |
Perform an action as specified by a Consumer object | void forEach(Consumer<? super T> action) |
The following table maps each of the operations the method processElements
performs with the corresponding aggregate operation:
下面的表格映射了方法processElements在相应的聚合操作中执行的每一个操作:
动作 | 聚合操作 |
获得一个对象 | Stream<E> stream() |
将这个对象和条件比对过滤 | Stream<T> filter(Predicate<? super T> predicate) |
将过滤后符合要求的对象映射给指定的Function对象 | <R> Stream<R> map(Function<? super T,? extends R> mapper) |
执行Consumer对象指定的动作 | 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.
操作符:filter、map和forEach都是聚合操作中的关键字。聚合操作的元素来自于一个stream对象,而不是直接来自集合(这就是为什么在本例中调用的第一个方法是stream)的原因。一个stream是一个元素序列。与集合不同的是,它不是存储元素的数据结构。相反,流通过管道从源(比如集合)中传递值。管道是stream操作序列,在本例中是filter-map-forEach。此外,聚合操作通常接受lambda表达式作为参数,使您能够定制它们的行为。
有关聚合操作的更详细的讨论,请参见聚合操作课程。
Lambda Expressions in GUI Applications(略)
现在谁用java写GUI。略略略略略……
比如这样:
btn.setOnAction(
event -> System.out.println("Hello World!")
);
Syntax of Lambda Expressions
A lambda expression consists of the following:
- 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.
- The arrow token, ->
- A body, which consists of a single expression or a statement block. This example uses the following expression:
lambda表达式由以下内容组成:
- 用括号括起来的逗号分隔格式的参数列表。Eg: (nt a, int b, Person p),比如 CheckPerson.test() 方法包含了一个参数p,Person p 代表了Person类的一个对象。
- 一个箭头 ->
- 方法体,由一个简单的表达式或者一个语句块组成。比如本例中:
-
// 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: // 如果您指定了单个表达式,那么jvm运行时就会评估这个表达式,然后返回它的值。或者,您可以使用return语句: p -> { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; }
-
/** * 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: * 注意:您可以省略lambda表达式中的参数的数据类型。 * 此外,如果只有一个参数,则可以省略括号。 * 例如,下面的lambda表达式也是有效的: */ p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
-
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: // 如果返回语句不是表达式; // 在lambda表达式中,您必须将语句括在括号中{} // 但是,您不需要将void方法包含在{}中。 // 比如下面的栗子 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表达式的一个例子,它接受不止一个表达式参数:
/**
* 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
* operateBinary方法,执行一个两个整数的数学运算
* 操作本身是由一个IntegerMath实例指定的
* 本栗中使用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));
}
}
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 any shadowing issues (see Shadowing for more information). Lambda expressions are lexically scoped. 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. The following example, LambdaScopeTest
, demonstrates this:
与本地和匿名类一样,lambda表达式可以捕获变量;它们对闭包的局部变量具有相同的访问权限。然而,与本地和匿名类不同的是,lambda表达式没有任何附加问题(更多信息请参阅附加问题部分)。Lambda表达式是词法作用域。这意味着它们不会从超类型中继承任何名称,也不会开启新的作用域。lambda表达式中的声明与当前作用域中的声明是一样的。下面的例子,lambda作用域测试,是这样的:
import java.util.function.Consumer;
/**
* This example generates the following output
* x = 23
* y = 23
* this.x = 1
* LambdaScopeTest.this.x = 0
*
*/
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);
}
}
// If you substitute the parameter x in place of y in the declaration of the lambda expression myConsumer,
// then the compiler generates an error:
// 如果你在lambda表达式myConsumer的声明中使用x替代y,就会报错
// 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 method methodInFirstLevel.
// To access variables in the enclosing class, use the keyword this.
// In this example, this.x refers to the member variable FirstLevel.x.
// 编译器会产生错误,“变量x已经在方法 methodInFirstLevel(int)中定义了。
// 因为lambda表达式没有引入新的作用域。
// 因此,您可以直接访问当前作用域范围内的字段、方法和局部变量。
// 例如,lambda表达式直接访问methodInFirstLevel方法的参数x
// 要访问当前类中的变量,请使用this关键字。
// 在这个例子中,this.x指的是成员变量FirstLevel.x。
Consumer<Integer> myConsumer = (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:
// 然而,就像本地和匿名类一样
// lambda表达式只能访问final修饰的局部变量和函数的参数。
// 例如,假设您在方法 methodInFirstLevel() 中添加以下赋值语句
// 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:
// 因为有了这个赋值语句,变量 FirstLevel.x 不再是final。
// 因此,Java编译器会生成一条错误消息 类似于“从lambda表达式引用的局部变量,必须是final类型 或 effectively final??不可修改?”。
// 在lambda表达式myConsumer试图访问变量FirstLevel.x时:
void methodInFirstLevel(int x) {
x = 99;
// ...
}
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:
如何确定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表达式在以下两种方法中使用:
// in Approach 3: Specify Search Criteria Code in a Local Class
public static void printPersons(List<Person> roster, CheckPerson tester)
// in Approach 6: Use Standard Functional Interfaces with Lambda Expressions
public void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester)
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:
当Java运行时调用方法printPersons时,它会期待CheckPerson的数据类型,因此lambda表达式是CheckPerson类型的。
然而,当Java运行时调用printPersonsWithPredicate
时,它会期望Predicate<Person>
中的
的Predicate<Person>
类型,所以lambda表达式是Predicate<Person>
类型的。
这些方法所期望的数据类型称为target类型。为了确定lambda表达式的类型,Java编译器使用所找到的lambda表达式的上下文或情境的目标类型。因此,您只能在Java编译器可以确定目标类型的情况下使用lambda表达式:
- 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.
对于方法参数,Java编译器用另外两种语言特性来确定目标类型:重载解析和类型参数推断。
Consider the following two functional interfaces
考虑以下两个功能性接口:
public interface Runnable {
void run();
}
public interface Callable<V> {
V call();
}
Runnable.run没有返回值,而Callable.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");
invoke(Callable)的方法将被调用,因为该方法返回一个值;方法invoke(Runnable)没有。在这种情况下,lambda expression () -> "done" 是 Callable<T>类型的。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.
如果它的目标类型和捕获的参数是可序列化的,那么您可以序列化lambda表达式。然而,就像内部类一样,lambda表达式的序列化是非常不鼓励的。
总结:官方文档还是写得好!!!!!!!^O^,我个人认为lambda表达式和策略模式还是有些区别的,首先他们都实现了封装变化,其次不同于策略的面向接口编程,lambda是面向功能接口编程,可以说是面向功能编程,最后不同于策略的一组策略实现的组合,lambda的不同实现之间不能构成一个整体,彼此之间不能结合,不能拆分,没有组合关系。