Lambda 表达式学习感受
Lambda 表达式是从 JDK 1.8 开始的,但是很多技术的诞生其实并不是凭空出现的,而是为了解决某种问题而逐步迭代出现的。
某位大佬级别的曾经用物理的第一性原理来讲软件开发技术,也讲出了学习技术的本质。虽然明白了很多的道理,但是很多时候我在学习的时候还是没办法站在一个思想高度较高的角度去认识和学习相应的技术。这点也许需要很多的编程经验才能够达到吧。
最近在学习 Lambda 表达式的时候也体会到了技术迭代的感觉,而讲解 Lambda 表达式前也给出了相应的一些可供参考的代码。我自己也把代码敲了一遍,虽然代码看起来很简单,但是实际上还是能从思想上提高很多的。
01 实现一个简单的需求并抛出问题
在开始学习 Lambda 表达式之前,首先有一个简单的需求,有一个 Student 类,其中有姓名、年龄和分数三个属性,要求实现查询年龄大于 14 岁的学生并进行输出,和分数大于 75 分的学生并进行输出,这样的两个方法。
这个需求看似还是很简单的,代码如下:
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("zhangsan", 14, 67));
list.add(new Student("lisi", 13, 77));
list.add(new Student("wangwu", 15, 97));
list.add(new Student("maliu", 19, 57));
// 查找年龄大于 14 的学生
findByAge(list);
// 查找分数大于 75 的学生
System.out.println("------------");
findByScore(list);
}
public static void findByAge(ArrayList<Student> students) {
ArrayList<Student> list = new ArrayList<>();
for (Student stu : students) {
if (stu.getAge() > 14) {
list.add(stu);
}
}
for (Student student : list) {
System.out.println(student);
}
}
public static void findByScore(ArrayList<Student> students) {
ArrayList<Student> list = new ArrayList<>();
for (Student stu : students) {
if (stu.getScore() > 75) {
list.add(stu);
}
}
for (Student student : list) {
System.out.println(student);
}
}
上面的代码很好理解,分别实现了 findByAge 和 findByScore 两个方法,看似没有问题的两个方法,其实问题还是不少的。在这两个方法中,除了 getAge 和 getScore 这两个取值和比较条件以外,代码都是重复的。显然不符合 DRY 原则。而且当如果增加新的需求的时候,比如判断名字的长度来进行筛选的时候,那么就会再实现一个相似的方法,但是方法中仍然存在大量的重复代码。
02 使用策略模式进行封装
在上面的代码中提出了代码重复的问题,因此这些代码是需要进行重构的。这里可以使用设计模式中的策略模式来解决比较分数和年龄的两个需求;另外还需要把循环打印的功能也进行封装。
首先定义一个只有一个方法的接口,然后分别再定义两个实现该接口的类,并且将上面代码的比较代码放到实现接口类的实现方法中。部分代码如下:
public boolean compare(Student student) {
return student.getAge() > 14;
}
通过接口和两个具体的实现类,就完成了策略模式。关于循环打印的输出的封装,定义在测试类中即可。
代码如下:
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("zhangsan", 14, 67));
list.add(new Student("lisi", 13, 77));
list.add(new Student("wangwu", 15, 97));
list.add(new Student("maliu", 19, 57));
getByFilter(list, new AgeFilter());
System.out.println("----");
getByFilter(list, new ScoreFilter());
}
public static void getByFilter(ArrayList<Student> students, StudentFilter studentFilter) {
ArrayList<Student> list = new ArrayList<>();
for (Student student : students) {
if (studentFilter.compare(student)) {
list.add(student);
}
}
printStudent(list);
}
在上面的代码中,定义了一个统一的方法 getByFilter 用来完成年龄和成绩的遍历,在调用的时候传入实际的接口实现类即可。
打印输出的代码封装了一个独立的方法,方法名为 printStudent,可以直接进行调用。
当要进行其他属性进行筛选的时候,仍然可以定义一个类来实现同样的接口。
上面的代码基本上是算是可以了,但是给每个需要查询的属性定义一个实现接口的类又显得过于麻烦,因此可以使用匿名内部类来进行改进。
03 匿名内部类来解决频繁定义实现接口的类
由于可以筛选的属性可能比较多,而每个属性实现一个类还是比较麻烦的,因此使用匿名内部类的方式进行一个实现就显得比较方便了,代码如下:
getByFilter(list, new StudentFilter() {
@Override
public boolean compare(Student student) {
return student.getAge() > 14;
}
});
System.out.println("----");
getByFilter(list, new StudentFilter() {
@Override
public boolean compare(Student student) {
return student.getScore() > 75;
}
});
System.out.println("----");
getByFilter(list, new StudentFilter() {
@Override
public boolean compare(Student student) {
return student.getName().length() > 5;
}
});
从上面的代码中可以看到,我们不但实现了前面的两个需求,还增加了一个查找名字长度大于 5 的这么一个需求,由于使用的是匿名内部类,因此没有再去单独定义一个类,这样看起来就省事了许多。
虽然省事了,但是这个接口就这么一个方法,而且其中方法体的代码也不多,是否有更简便的方式来写这段代码呢,其实还是有的。
04 Lambda 表达式出场
Lambda 表达式是 JDK 1.8 的一个新功能,它能让代码更加的简洁,而且对于集合的遍历、迭代、以及一些常用的操作更加的方便。
我们使用 Lambda 表达式来改进上面的代码,代码如下:
getByFilter(list, (e)->e.getAge() > 14);
System.out.println("----");
getByFilter(list, (e)->e.getScore() > 75);
System.out.println("----");
getByFilter(list, (e)->e.getName().length() > 5);
可以看到,所有使用匿名内部类的部分,都被替换掉了,从其中的一些属性和比较符大体能够看出和上面的代码还是相似的。但是,相应的代码少了许多,看起来也优雅了许多。
总结
Lambda 表达式不仅仅只能实现这些功能,这只是通过解决实际的一个问题来逐步的引出了 Lambda 表达式。也侧面体会到了技术的升级和迭代在中间都是有一个过程和思考的。
关于 Lambda 表达式的学习其实感触还是很多的,暂且先总结这些吧!!!
微信中搜索 “码农UP2U” 关注我的公众号吧!!!