Lambda教程
OverView
- 创建方法用来匹配指定特点的成员
- 创建更加通用的方法
- 在本地类中指定搜索条件
- 使用匿名类指定搜索条件
- 使用lambda表达式搜索条件
- 使用lambda表达式实现标准的功能性接口
- 在你的应用中使用Lambda
- 广泛地使用泛型
- Lambda语法
想象一下,有这样的一个需求,需要从List<Person>
队列中查找出特定的Person
成员
public class Person {
public enum Sex {
MALE, FEMALE
}
String name;
LocalDate birthday;
Sex gender;
String emailAddress;
public int getAge() {
// ...
}
public void printPerson() {
// ...
}
}
创建方法用来匹配指定特点的成员
public static void printPersonsOlderThan(List<Person> roster, int age) {
for (Person p : roster) {
if (p.getAge() >= age) {
p.printPerson();
}
}
}
创建更加通用的方法
public static void printPersonsWithinAgeRange(
List<Person> roster, int low, int high) {
for (Person p : roster) {
if (low <= p.getAge() && p.getAge() < high) {
p.printPerson();
}
}
}
在类中指定搜索条件
public static void printPersons(
List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
CheckPerson
接口定义
interface CheckPerson {
boolean test(Person p);
}
CheckPerson
实现
class CheckPersonEligibleForSelectiveService implements CheckPerson {
public boolean test(Person p) {
return p.gender == Person.Sex.MALE &&
p.getAge() >= 18 &&
p.getAge() <= 25;
}
}
使用
public static void printPersons(
roster, new CheckPersonEligibleForSelectiveService());
使用匿名内部类指定搜索条件
public static void printPersons(
roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);
使用匿名内部类现对上面来说减少了不少代码,因为不用每次查找的时候都要新建一个类,然而考虑这个接口只有一个方法,这样看起来有点臃肿,这种情况下就可以考虑使用Lambda表达式来替代匿名内部类
使用lambda表达式搜索条件
CheckPerson
只是一个功能性接口,功能性接口通常只有一个抽象方法(功能性接口可以包含多个默认方法或者静态方法)。因为这样,所以可以在实现的时候省略方法名。
如下使用lambda表达式来代替匿名内部类:
public static void printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
使用lambda表达式实现标准的功能性接口
重新考虑下接口CheckPerson
interface CheckPerson {
boolean test(Person p);
}
这个功能性接口只有一个方法,并且参数也只有一个和返回一个Boolean的结果。这样的接口好简单,JDK默认提供了几个功能性接口,可以在java.util.function
下找到。
例如,可以使用Predicate<T>
接口替代CheckPerson
:
interface Predicate<T> {
boolean test(T t);
}
方法定义
public static void printPersonsWithPredicate(
List<Person> roster, Predicate<Person> tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
使用Lambda表达式
printPersonsWithPredicate(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
在你的应用中使用Lambda
重新考虑如下方法,想想可以在哪些地方使用Lambda表达式
public static void printPersonsWithPredicate(
List<Person> roster, Predicate<Person> tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
记住要使用Lambda表达式, 一定 要实现带一个抽象方法的功能性接口。
下面的方法,使用Consumer<Person>
接口的实例的accept
方法替代printPerson
方法来打印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);//简单地代替打印方法
}
}
}
以下方法使用Lambda表达式打印
processPersons(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25,
p -> p.printPerson()
);
假设你想验证Person
的有效性或者获取它的联系方式?你可以需要一个这样功能性接口Function<T,R>
并有一个方法R apply(T t)
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); //打印信息
}
}
}
如下则是使用Lambda表达式的效果
processPersonsWithFunction(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25,
p -> p.getEmailAddress(),
email -> System.out.println(email)
);
广泛地使用泛型
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);
}
}
}
使用Lambda表达式的方式和上一节一样
Lambda语法
1.一个在括号内已逗号分隔的形式参数
例如CheckPerson
接口的test
方法包含一个参数,p代表Person类的一个实例。
注意:在Lambda表达式中可以省略数据类型,另外如果只有一个参数,可以省略括号
例如:
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
2.->标志
3.Body,只有一句表达式或语句块
例如:
p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
如果你使用一个表达式,JVM会计算表达式的值并返回。当然,你还是可以使用return,
return声明并不是一个表达式,在Lambda中,你必须使用{}来围绕声明。
p -> {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;//可以去掉return,{}也可以去掉
}
如果返回类型是Void,可以忽略{},如:
email -> System.out.println(email)
完整实例
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));
}
}