开发环境
- eclipse 4.7.3a
- jdk 9
案例
通常我们在开发过程中会遇到如下需求:
需求1:找到大于指定年龄的用户,并输出他们的个人信息
public static void printPersonsOlderThan(List roster, int age) {
for (Person p : roster) {
if (p.getAge() >= age) {
p.printPerson();
}
}
}
需求2:找到指定指定年龄段的用户,并输出他们的个人信息
public static void printPersonsWithinAgeRange(
List roster, int low, int high) {
for (Person p : roster) {
if (low <= p.getAge() && p.getAge() < high) {
p.printPerson();
}
}
}
通过观察发现,我们可以定义一个抽象接口,返回符合特定信息的用户信息:
interface CheckPerson {
boolean test(Person p);
}
将上述方法进行重构:
// 传入CheckPerson 来返回特定信息的用户
printPersons(
List roster,
CheckPerson checkPerson
);
//
在调用时可以通过匿名内部类方式实例化接口(老司机通常会这样 ~ ~)
// 找到指定指定年龄段的用户
printPersons(
List roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);
使用匿名类的一个问题是,如果匿名类的实现非常简单,例如只包含一个方法的接口,那么匿名类的语法可能看起来过于繁琐,还有没有更简洁的写法呢?
关于Lambada表达式
Lambda 是一个匿名函数,即没有函数名的函数 [Lambda表达式]。 Java 8 引入了 Lambda 表达式, 它允许把函数作为一个方法的参数,使用 Lambda 表达式使代码看起来更加"简洁紧凑"(有待考证 ~ ~)。
使用Lambda表达式调用printPersons方法:
printPersons(
roster,
(p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
我们发现匿名内部类被替换成了如下格式:
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
它还可以这样写:
(Person p) ->{ p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;}
类和方法的声明被省略,只保留了方法的入参
(Person p) // 方法的入参
-> // 箭头的右边是方法的具体实现
访问局部变量
Lambda 可以访问局部变量,但不能访问局部变量表达式,否则会导致编译异常
int x = 0;
// 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 myConsumer = (y) ->
{
System.out.println("x = " + x); // Statement A
System.out.println("y = " + y);
System.out.println("this.x = " + x);
System.out.println("LambdaTest.this.x = " + demo.field0);
};
Lambda的返回类型和方法参数
我们来分别研究Runnable接口和Callable接口,其中Runnable 接口没有入参也不需要返回值,Callable接口也不需要入参,返回指定泛型类型。
Tips: Runnable 和 Callable都用于线程实现,Callable可以阻塞等待线程返回结果。
public interface Runnable {
void run();
}
public interface Callable {
V call();
}
对应的lambda表达式如下:
Runnable r = () ->{ System.out.println("haha");};
Callable c = () ->{ return "done";};
总结
要确定lambda表达式的类型,Java编译器将使用发现lambda表达式的上下文或情境的目标类型。因此只能在Java编译器可以确定目标类型的情况下使用lambda表达式:
- 变量声明
- 分配
- 返回值
- 数组初始化器
- 方法或构造函数参数
- Lambda表达体
- 三元条件表达式
- 类型转换表达式
下一篇:方法引用
Github工程地址