1.典型的用Lambda表达式的场景
如果有这样的一个小应用,其中的一个类Student包含姓名(name),性别(sex),分数(score),如下:
package demo; public class Student { public enum Sex { MALE,FEMALE } private String name; private Sex gender; private int age; private double score; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Student(String name, Sex gender, int age, double score) { super(); this.name = name; this.gender = gender; this.age = age; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Sex getGender() { return gender; } public void setGender(Sex gender) { this.gender = gender; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } public void printStudent() { System.out.println("学生姓名:"+this.name); System.out.println("学生年龄:"+this.age); System.out.println("学生性别:"+this.gender); System.out.println("学生成绩:"+this.score); } }
假设创建的学生类都放在一个Student的ArrayList<Student>,现如今我们要实现这样的一个功能,输入age,筛选出年龄大于age的学生。这非常简单:
(1) 创建一个方法寻找符合条件的Student对象:
public void printStudnetOlderThan(int age) { for(Student stu:studentList) { if (stu.getAge()>age) { stu.printStudent(); } } }
这种方法会使你的application变得非常脆弱,如果你的需求要进行修改,比如筛选出成年人,成绩优秀的,等等,那么你将会重写很多的代码,而且代码将会显得很混乱,给代码的阅读带来不便。
(2) 将筛选条件写在一个独立的类中。
package demo; public interface CheckStudent { public boolean test(Student stu); } package demo; public class CheckStudentForOlder implements CheckStudent { @Override public boolean test(Student stu) { if (stu.getAge()>18) return true; return false; } }
public void printStudnetOlderThan(CheckStudent tester) { for (Student stu:studentList) { if(tester.test(stu)) { stu.printStudent(); } } }
这样就不会出现printStudentOlderThan函数因为筛选条件的改变,而重写多次。但是缺点也是显而易见的你必须为你的每一个筛选条件都创建一个继承CheckStudnet的类,虽然这样代码是比第一中方法更加有条理,但还是显得很臃肿。
(3)创建匿名类
mock.printStudnetOlderThan(new CheckStudent() { @Override public boolean test(Student stu) { if (stu.getAge()>18) return true; return false; } });
这运用了java匿名对象的特性,相比于上一种创建大量筛选类的方法来说,已经帮我们节省了大量创建类的繁琐代码。但是给人的感觉不是很好,代码的功能并不是很明显。
(4)利用Lambda表达式
这个CheckStudent接口是一个function interface(只有一个抽象方法),因此你可以在继承它时,省略掉这个方法的名字,你可以使用Lambda表达式
mock.printStudnetOlderThan((Student p)->p.getAge()>20);
(5) 使用一个标准的Function Interfaces 用于Lambda表达式
JDK定义了几个标准Function Interfaces(只包含一个抽象方法),你可以在 java.util.function
.中找到他。
这样就可以避免创建一个接口了。
public void printStudnetOlderThan(Predicate<Student> tester) { for (Student stu:studentList) { if(tester.test(stu)) { stu.printStudent(); } } }
//调用上面的函数
mock.printStudnetOlderThan((Student p)->p.getAge()>20);
(6) 使用Consumer<T>来优化代码
我们可以根据上面的代码使得筛选条件可以用Lambda表达式快速的更换,使得程序更加健壮,但是如果在满足筛选条件时,我们想执行的不是printStudent方法,而是其他方法呢?这时,Consumer就可以帮助我们解决这个问题了。
public void printStudnetOlderThan(Predicate<Student> tester,Consumer<Student> block) { for (Student stu:studentList) { if(tester.test(stu)) { block.accept(stu); } } } //调用 mock.printStudnetOlderThan((Student p)->p.getAge()>20, p->p.printStudent());
2.Lambda表达式的语法:
(1) 一个带有()用”,”分割的参数列表,你可以省略参数的数据类型。另外如果只有一 个参数你也可以省略掉括号
(2) 箭头符号:->
(3) 用单个表达式或语句块组成的主体。这里需注意如果是语句块要用{}括起来。
3.Target Typing
你怎么样确定一个Lambda 表达式的类型。在上面的例子中我们在方法中调用Lambda表达式,分别用了 Function Interface ,Predicate,Consumer,我们还可以用Function,
只有在java的编译器可以通过上下文找到Lambda表达式的Target Type,你才可以使用Lambda表达式。