首先,我们来谈一谈为什么java jdk8会引入lambda表达式,下面我们定义一个功能,
public class Person {
public enum Sex {
man,woman
}
private String name;
private LocalDate birthday;
private Sex gender;
private String emailAddress;
private int age;
......set......get method
}
然后上我们的主业务代码:
public static void main(String[] args) {
List<Person> linkedList = new LinkedList<>();
linkedList.add(new Person("weijinhao",null,Person.Sex.man, "740876747@qq.com", 10));
linkedList.add(new Person("liuyawei",null,Person.Sex.man, "111111111@qq.com", 13));
printPersonsOlderThan(linkedList, 10);
}
/*
* 该函数算是我们的主要业务,再者里面我们进行集合的遍历,并筛选出相关的元素,然后执行相关的操作。
* 我们的lambda表达式主要是就是优化这边。
*/
public static void printPersonsOlderThan(List<Person> list,int age) {
for (Person p : list) {
if (p.getAge() >= age) {
System.out.println(p.toString());
}
}
}
}
好,这个业务也是可以实现的,但是我们要是想更换筛选条件的话怎么办,重新写代码实现吗?当然不是,我们可以分出一个
只包含test函数的接口,然后用内部类去实现筛选。
public static void main(String[] args) {
List<Person> linkedList = new LinkedList<>();
linkedList.add(new Person("weijinhao",null,Person.Sex.man, "740876747@qq.com", 10));
linkedList.add(new Person("liuyawei",null,Person.Sex.man, "111111111@qq.com", 13));
//此处我们使用匿名内部类来实现筛选业务的分离
printPersonsOlderThan(linkedList, new CheckPerson() {
@Override
public boolean test(Person p) {
//相关筛选的分离
return p.getGender() == Person.Sex.man
&& p.getAge() > 10
&& p.getAge() < 25;
}
});
}
/*
* 该函数算是我们的主要业务,再者里面我们进行集合的遍历,并筛选出相关的元素,然后执行相关的操作。
* 我们的lambda表达式主要是就是优化这边。
*/
public static void printPersonsOlderThan(List<Person> list,CheckPerson test) {
for (Person p : list) {
if (test.test(p)) {
System.out.println(p.toString());
}
}
}
但是这样看起来是不是纷繁复杂呢,现在就是Lambda表达式发挥水平的时候了。
public static void main(String[] args) {
List<Person> linkedList = new LinkedList<>();
linkedList.add(new Person("weijinhao",null,Person.Sex.man, "740876747@qq.com", 10));
linkedList.add(new Person("liuyawei",null,Person.Sex.man, "111111111@qq.com", 13));
//此处我们使用匿名内部类来实现筛选业务的分离
printPersonsOlderThan(linkedList, /*Lambda表达式*/ (Person p) ->
p.getGender() == Person.Sex.man
&& p.getAge() > 10
&& p.getAge() < 25
);
}
看到了吗,我们可以实现一样的操作,但是不需要匿名内部类那样复杂。这里需要提到的时,Lambda表达式实现的基础是函数式接口,对滴,就是我们前面抽取的CheckPerson接口,下面我们在提函数式接口的格式。这里先不管。
接下来我们接着优化。
难道我们要想使用Lambda表达式就必须得自己写一个函数式接口吗,那也太费劲了吧,你想到了,人家也能想到,早就给你定义出来了一堆标准函数式接口,你直接拿过来用就ok了,他们在java.util.function包当中。在用时找到适合自己的就行了。下面我们就使用标准函数式接口来实现吧。
/*
* 该函数算是我们的主要业务,再者里面我们进行集合的遍历,并筛选出相关的元素,然后执行相关的操作。
* 我们的lambda表达式主要是就是优化这边。
*/
public static void printPersonsOlderThan(List<Person> list,Predicate<Person> test) {
for (Person p : list) {
if (test.test(p)) {
System.out.println(p.toString());
}
}
}
然后看看还有什么需要优化的,这里我们可不可以把我们的相关操作拿出来呢,首先我们去标准函数式包中查找适合我们的函数接口。
public static void main(String[] args) {
List<Person> linkedList = new LinkedList<>();
linkedList.add(new Person("weijinhao",null,Person.Sex.man, "740876747@qq.com", 10));
linkedList.add(new Person("liuyawei",null,Person.Sex.man, "111111111@qq.com", 13));
//此处我们使用匿名内部类来实现筛选业务的分离
printPersonsOlderThan(
linkedList,
/*Lambda表达式*/
(Person p) -> p.getGender() == Person.Sex.man
&& p.getAge() > 10
&& p.getAge() < 25,
(Person p) -> System.out.println(p.toString())
);
}
/*
* 该函数算是我们的主要业务,再者里面我们进行集合的遍历,并筛选出相关的元素,然后执行相关的操作。
* 我们的lambda表达式主要是就是优化这边。
*/
public static void printPersonsOlderThan(List<Person> list,Predicate<Person> test,Consumer<Person> operation) {
for (Person p : list) {
if (test.test(p)) {
// System.out.println(p.toString());
operation.accept(p);//我们把操作也拿出来了
}
}
}
那但是感觉还是不够通用,如果我们加上泛型的话感觉会更好。
public static<T> void printPersonsOlderThan(Iterable<T> list,Predicate<T> test,Consumer<T> operation) {
for (T p : list) {
if (test.test(p)) {
// System.out.println(p.toString());
operation.accept(p);//我们把操作也拿出来了
}
}
}
ok,我们最终的函数就形成了。就这事泛型和Lambda的功能。
既然Lambda这麽强大,下面我们就来说说Lambda的格式:
java当中使用Lambda表达式的前提是:必须有函数式接口(有且仅有一个抽象方法的接口,叫做函数式接口)。
那么如何才能万无一失的检测一下当前的接口是不是函数式接口呢?我们可以使用注释
用一个固定的格式写在public interface 之前一行即可。
@FunctionalInterface
public interface className {
}
好了,我们来看看Lambda表达式的基本格式:
//用逗号分隔的括号内的形式参数列表(type param1,type param2) -> {代码块}
我们可以忽略参数的类型,如果只有一个参数的话可以忽略大括号;
代码块中只有单句的话可以去掉{ } ,如果我们需要返回表达式的值,使用return(如果是单句的话,可以忽略return ,java会计算表达式,并返回代码块的值)
Lambda表达式在作用域中访问本地变量
像内部类一样,Lambda表达式可以抓取本地变量,但是需要注意的是,Lambda表达式在使用时不具有隐藏同名变量的作用域功能,还有就是他不能获取父类或相关类的变量,让我们附上一段代码来测试一下:
public class LambdaScopeTest {
private int x = 0;
public class FirstLevel {
int x = 1;
public void methodInFirstLevel(int x) {
Consumer<Integer> consumer = y -> //如果我们在此使用x的话会报错,
//这是应为Lambda表达式在使用时不具有隐藏同名变量的作用域功能
{
System.out.println("x:" + x); //这里的访问和内部类一样,但是不能关联更深的层次
System.out.println("y:" + y);
System.out.println("this.x:" + this.x);
System.out.println("LambdaScopeTest.x:" + LambdaScopeTest.this.x);
};
consumer.accept(x);
}
}
}
public static void main(String[] args) {
LambdaScopeTest lambdaScopeTest = new LambdaScopeTest();
LambdaScopeTest.FirstLevel firstLevel = lambdaScopeTest.new FirstLevel();
firstLevel.methodInFirstLevel(23);
}
下面我们查看一下打印的结果:
//和我们介绍的一样
x:23
y:23
this.x:1
LambdaScopeTest.x:0
下面我们在来介绍一下目标类型:
我们的Lambda表达式可以应用到随意的函数式接口吗?答案是不能,这是应为java编译器会根据Lambda表达式的上下文环境去判断,但是我们可以大体的认为是根据参数和返回值去确定。
好了这就是Lambda表达式的全部介绍了,谢谢大家的耐心观看!分享技术,感悟人生!