- 概述
- 背景
- 例子
- 使用Lambda表达式改进代码
- 总结
概述
Lambda表达式是Java8的一个重要的新特性,它使用一个表达式来表示一个方法接口。它还改进了Collection库,新的并发特性也提升了在多核环境下的性能。
背景
匿名内部类
首先还要先从匿名内部类说起,如果一个类只出现一次,那么可以使用如下方式实现:
JButton testButton = new JButton("Test Button");
testButton.addActionListener(new ActionListener(){
@Override public void actionPerformed(ActionEvent ae){
System.out.println("Click Detected by Anon Class");
}
});
上面这段代码可读性不是很好,也不够优雅。
功能性接口
ActionListener接口
package java.awt.event;
import java.util.EventListener;
public interface ActionListener extends EventListener {
public void actionPerformed(ActionEvent e);
}
像ActionListener只有一个方法的接口叫功能性接口,比如 Runnable 和 Comparator.
Lambda表达式语法
一个Lambda表达式由以下三部分组成:
Argument list | Arrow token | Body |
---|---|---|
(int x, int y) | -> | x+y |
body可以是一个表达式或者一个语句块。如果是表达式的形式,就执行body的代码并返回;如果是代码块的形式,执行完之后会把控制权交给调用方。
代码块的形式:
(String s) -> { System.out.println(s); }
表达式的形式:
(int x, int y) -> x + y
() -> 42
例子
下面是Comparator的例子,使用了内部类和lambda表达式。
public class Person {
private String givenName;
private String surName;
private int age;
private Gender gender;
private String eMail;
private String phone;
private String address;
/** getter , setter and other methods **/
}
List<Person> personList = Person.createShortList();
// Sort with Inner Class
Collections.sort(personList, new Comparator<Person>(){
public int compare(Person p1, Person p2){
return p1.getSurName().compareTo(p2.getSurName());
}
});
System.out.println("=== Sorted Asc SurName ===");
for(Person p:personList){
p.printName();
}
// Use Lambda instead
// Print Asc
System.out.println("=== Sorted Asc SurName ===");
Collections.sort(personList, (Person p1, Person p2) -> p1.getSurName().compareTo(p2.getSurName()));
for(Person p:personList){
p.printName();
}
// Print Desc
System.out.println("=== Sorted Desc SurName ===");
Collections.sort(personList, (p1, p2) -> p2.getSurName().compareTo(p1.getSurName()));
for(Person p:personList){
p.printName();
}
可以注意到第二个lambda表达式没有声明参数类型,lambda支持“target typing”,从上下文推断出参数类型。这里因为Comparator定义的是泛型,编译器可以推断出这里的两个参数类型是Person。
使用Lambda表达式改进代码
理论上lambda应该支持DRY(Don't repeat yourself)原则,并且使代码可读性更好。
下面这个例子依旧使用上面的Person类。假设我们需要区分一个集合里的Person的身份:
- Drivers:age>=16
- Draftees: age>=18 && ages<=25的男性青年
- Pilots: age>=23 && ages<=65
下面这个类负责查找集合中的Driver、Draftee和Pilot
public class RoboContactMethods {
public void callDrivers(List<Person> pl){
for(Person p:pl){
if (p.getAge() >= 16){
roboCall(p);
}
}
}
public void emailDraftees(List<Person> pl){
for(Person p:pl){
if (p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE){
roboEmail(p);
}
}
}
public void mailPilots(List<Person> pl){
for(Person p:pl){
if (p.getAge() >= 23 && p.getAge() <= 65){
roboMail(p);
}
}
}
public void roboCall(Person p){
System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
}
public void roboEmail(Person p){
System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
}
public void roboMail(Person p){
System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
}
}
很明显上面这个类不符合DRY原则,每个方法都使用了循环,代码也不易维护。如果改变了查找的规则,那这段代码的改动会很大。
重构
Java SE 8提供了java.util.function包,有很多标准的功能性接口,比如Predicate:
public interface Predicate<T> {
public boolean test(T t);
}
下面是使用Predicate接口改进的RoboContactMethods类:
public class RoboContactLambda {
public void phoneContacts(List<Person> pl, Predicate<Person> pred){
for(Person p:pl){
if (pred.test(p)){
roboCall(p);
}
}
}
public void emailContacts(List<Person> pl, Predicate<Person> pred){
for(Person p:pl){
if (pred.test(p)){
roboEmail(p);
}
}
}
public void mailContacts(List<Person> pl, Predicate<Person> pred){
for(Person p:pl){
if (pred.test(p)){
roboMail(p);
}
}
}
public void roboCall(Person p){
System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
}
public void roboEmail(Person p){
System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
}
public void roboMail(Person p){
System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
}
}
调用:
List<Person> pl = Person.createShortList();
RoboContactLambda robo = new RoboContactLambda();
// Predicates
Predicate<Person> allDrivers = p -> p.getAge() >= 16;
Predicate<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE;
Predicate<Person> allPilots = p -> p.getAge() >= 23 && p.getAge() <= 65;
总结
本文主要介绍了Java的匿名内部类,使用lambda表达式代替匿名内部类,lambda表达式的正确语法,以及function包中Predicate接口。
网友评论