Java8 Lambda表达式
Lambda表达式是所有Java8新特性中比较重要的特性,同时也是一些后续新特性的基础,比如在Stream特性中就大量运用了这一新特性,本篇文章主要是个人的一些理解和示例代码
什么是Lambda表达式
个人的理解是一种匿名函数的语法,在功能上与匿名函数一致。lambda表达式为匿名函数定义了更简洁灵活的语法
为什么需要Lambda表达式
下面我们会使用基于Lambda表达式的函数式编程与传统编程进行对比,体会这种做法的妙处
- 需求:实现一个筛选学生的功能
- 创建实体类Student
package entity;
public class Student {
public Student() {
}
public Student(int id, int age, String name, int sex, int score) {
this.id = id;
this.age = age;
this.name = name;
this.sex = sex;
this.score = score;
}
private int id ;
private int age ;
private String name ;
private int sex ;
private int score ;
//getset 省略
public boolean isFirstStudent(){
return 1==this.getId();
}
}
- 如果你是一个有经验的程序员,你会对筛选这个功能做个抽象,然后根据业务不同的筛选逻辑分别实现接口,现在创建过滤接口
public interface StudentFilter {
boolean filter (Student student);
}
- 根据业务实现这个接口,比如根据年龄,又或者根据分数判断学生是否符合条件
public class SexFilter implements StudentFilter {
@Override
public boolean filter(Student student) {
if(student.getSex() ==1){
return true ;
}
return false;
}
}
public class AgeFilter implements StudentFilter {
@Override
public boolean filter(Student student) {
if(student.getAge()>20){
return true ;
}
return false;
}
}
- 下面是我们的主函数,根据不同的filter实现,获取学生的名字
public class OriMain {
public static void main(String[] args) {
List<Student> students = Arrays.asList(new Student(1,27,"wwh",1,99),
new Student(2,25,"yuachen",1,68),
new Student(3,29,"firstin",1,78),
new Student(4,26,"bgirl",0,20),
new Student(5,18,"sgirl",0,50));
//根据年龄过滤出姓名列表
List<String> names = getNameByFilter(students,new AgeFilter());
//根据性别过滤出姓名列表
List<String> name2 = getNameByFilter(students,new SexFilter());
}
private static List<String> getNameByFilter(List<Student> students, StudentFilter filter) {
List<String> result = new ArrayList<>();
for (Student stu : students) {
String name = stu.getName();
if(filter.filter(stu)){
result.add(name);
}
}
return result;
}
}
- 如果这时候我们又需要挑选出得分高于80分的学生姓名,我们又需要实现一遍接口,会使代码量很大,累很多,维护困难,或者你不想为了几行代码创建一个实现类,采用匿名内部类的形式如下。但是这种方式往往看上去很笨重啰嗦
List<String> names = getNameByFilter(students, new StudentFilter() {
@Override
public boolean test(Student student) {
return student.getScore()>80;
}
});
想要灵活试试Lambda表达式
从下面的代码量就可以感受到代码灵活了不少,语义也更加明确
public class LambdaDemo {
public static void main(String[] args) {
List<Student> students = Arrays.asList(new Student(1,27,"wwh",1,99),
new Student(2,25,"yuachen",1,68),
new Student(3,29,"firstin",1,78),
new Student(4,26,"bgirl",0,20),
new Student(5,18,"sgirl",0,50));
//可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式
List<String> nameList = getNameByLambda(students,(student)->student.getAge()>20);
//获取id为一的学生姓名
List<String> nameList2 = getNameByLambda(students,(student)->student.isFirstStudent());
//方法引用
List<String> nameList3 = getNameByLambda(students,Student::isFirstStudent);
printStudentList(students);
}
/**
*函数式接口:
* 定义 函数式接口就是只定义一个抽象方法的接口
* 注解 @FunctionalInterface
*
* Lambda表达式可以被赋给一个
* 变量,或传递给一个接受函数式接口作为参数的方法就好了,当然这个Lambda表达式的签名要
* 和函数式接口的抽象方法一样
*/
private static List<String> getNameByLambda(List<Student> students, Predicate<Student> predicate) {
List<String> result = new ArrayList<>();
for (Student stu : students) {
String name = stu.getName();
if(predicate.test(stu)){
result.add(name);
}
}
return result;
}
//Stream API的使用,后续会介绍
private static void printStudentList(List<Student> stringList) {
stringList.stream().map(Student::getName).forEach(System.out::println);
}
}
什么场景适合运用Lambda表达式让代码更灵活
public class LambdaDemo {
public static void main(String[] args) {
//场景一:线程创建
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("create thread");
}
});
t.start();
Thread t2 = new Thread(()->{
System.out.println("createBy Lambda");
});
t2.start();
//场景二:sort方法
List<Student> students = Arrays.asList(new Student(1,27,"wwh",1,99),
new Student(2,25,"yuachen",1,68),
new Student(3,29,"firstin",1,78),
new Student(4,26,"bgirl",0,20),
new Student(5,18,"sgirl",0,50));
//sort应用
students.sort((a,b)->a.getAge()-b.getAge());
//方法引用就是让你根据已有的方法实现来创建Lambda表达式
students.sort(comparing(Student::getAge));
//场景三:stream API中应用
students.stream().map(Student::getName).forEach(System.out::println);
}
}
java8提供的一些函数式接口
四大核心函数式接口:
- Predicate<T> : 断言型接口 定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean
- Consumer<T> : 消费型接口 定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回void
- Supplier<T>: 供给型接口 定义了一个名叫get的抽象方法,它不接受参数,并返回一个对象T
- Function<T,R> : 函数型接口 Function接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象