Lambda表达式应用浅析
一、什么是Lambda表达式
Lambda表达式是Java8的一个新特性,它提供了一种更加清晰和简明的方式使用单一方法接口(SAM,即Simple Abstract Method.)。使用Lambda表达式能够更加方便和简单的使用匿名内部类,比如对于集合的遍历、比较、过滤等等。
二、知识背景
Annoymous Inner class
在Java里面,可以在使用的时候声明一个类的对象,并在使用时给出类的某些方法的实现,这种类叫做匿名内部类,比如在Android中的OnClickListener,代码如下:
如上代码所示,new View.OnClickListener()就是一个匿名内部类。
Functional interface
功能接口,只含有一个抽象方法的接口,即刚才所说的SAM,单一抽象方法。这样的接口比如:
-
文件过滤接口-java.io.FileFilter public abstract boolean accept(File pathname);
-
比较接口-java.lang.Comarable int compareTo(T another);
-
Android的OnClickListener接口-android.view.View.OnClickListener onClick(View v);
三、Lambda表达式应用
通过上面的内容,可以对lambda表达式有一个简单的认识:lambda是一种能够极大的简化声明和使用功能性接口的表达式语法,通过使用lambda表达式,可以使得代码更加的清晰和简洁。接下来我们就来学习一下如何使用lambda。
**Lambda Syntax
**每个lambda都包括以下三个部分:
Argument List-(int x,int y)
Arrow Token - ->
Body - x+y
Argument部分是调用SAM方法时的入口参数;Arrow Token固定为->;Body内部可以是简单的表达式,也可是复杂的代码块。以上的例子仅仅是一个简单的表达式,用来返回输入的x和y的和,如果参数不匹配或者不是单一抽象接口都会出现编译错误。
Lambda Examples
1. Runnable Lambda
使用匿名内部类代码如下:
public static void testRunnable(){
Thread thread = new Thread(new Runnable(){
System.out.println(“In ”);
});
}
使用lambda表达式代码如下
public static void testRunnable(){
Thread thread = new Thread(() -> {
System.out.println(“In ”);
});
}
2.FileFilter
使用匿名内部类代码如下:
public static void testFileFilter(String path){
File file = new File(path);
FileFilter filter = new FileFilter(){
public boolean accept(File pathName){
return pathName.isDirectory();
}
};
File\[\] files = file.listFiles(filter);
}
public static void testFileFilter(String path){
File file = new File(path);
file.listFiles((file)->file.isDirectory);
}
3.Comparator
private static void testComparator(){
Person person1 = new Person();
person1.setAge(10);
Person person2 = new Person();
person2.setAge(90);
Person person3 = new Person();
person3.setAge(26);
List<Person> persons = new ArrayList<>();
persons.add(person1);
persons.add(person2);
persons.add(person3);
Collections.sort(persons,(p1,p2) -> p1.getAge() - p2.getAge());
for(Person p : persons){
System.out.println(p.getAge());
}
}
在这个例子当中,lambda作为Comparator参数传入sort方法,有人可能会有疑问,Comparator接口中并非只有一个simple abstract method ,即该接口不是刚才说过的Functional Interface。我们打开java.lang.Comparator接口,发现接口名字上有个标注@Functational Interface,进一步看这个标注的详细介绍会发现,该接口是从java8开始加入的,目的是声明某个接口为Functional Interface。如果该接口不是Functional Interface,即拥有超过一个抽象方法,则会抛出编译错误。而这里Comparator中还有一个抽象方法名为boolean equals(Object obj),从文档中可以看出,如果某个接口拥有一个抽象方法,同时又覆盖了Object的public方法,那么这个抽象方法并不会计算在该接口的抽象方法数量中。也就是说该接口还是一个Functional Interface。
四、Lambda Advantages
通过以上的几个例子,大家对lambda已经有了一定的认识,lambda除了能够简化代码行数之外,还能够使得我们的代码遵从Don’ Repeat Yourself(DRY)准则,提高代码的可读性和简洁程度。
(1)情景:现在某公司年底需要给公司的员工发一些奖励,奖励程度和在公司工作的年数有关,规则如下:a.工作1年以内的-发放2000元;b.工作1-3年的-发放5000元;c.工作3-5年的-发放10000元;d.工作5年以上的-发放20000元。但是针对以上四类员工,年底可能会根据一定规则只针对四类中的一类员工进行奖励。
(2)代码初步实现,如下:
定义实体类:Staff.java
public class Staff{
private int staffId;
private int workYears;
private int name;
public int getWorkYears(){
return this.workYears;
}
public String getName(){
}
public void setWorkYears(int workYears){
this.workYears = workYears;
}
public void setName(String name){
this.name = name;
}
}
定义生产数据的静态方法
public class StaffUtil{
public static List<Staff> createStaffList(int count){
List<Staff> staffList = new ArrayList<Staff>();
for(int i=0;i<count;i++){
Staff staff = new Staff();
staff.setName(“Kevin”+i);
staff.setWorkYears(i);
staffList.add(staff);
}
}
}
定义测试类
public class TestLambda{
public static void main(String\[\] args){
List staffList = StaffUtil.createStaffList(20);
filterA(staffList);
}
public static void filterA(List<Staff> staffList){
for(Staff staff:staffList){
if(staff.getWorkYears() <= 1){
sendMoney(staff,2000);
}
}
}
public static void filterB(List<Staff> staffList){
for(Staff staff:staffList){
if(staff.getWorkYears() <= 3 && staff.getWorkYears() > 1){
sendMoney(staff,5000);
}
}
}
public static void filterC(List<Staff> staffList){
for(Staff staff:staffList){
if(staff.getWorkYears() <= 5 && staff.getWorkYears() > 3){
sendMoney(staff,10000);
}
}
}
public static void filterD(List<Staff> staffList){
for(Staff staff:staffList){
if(staff.getWorkYears() <= 5 && staff.getWorkYears() > 3){
sendMoney(staff,10000);
}
}
}
public static void sendMoney(Staff staff,int money){
System.out.println(“发放”+staff.getName()+”奖金”+money+”元”);
}
}
以上代码逻辑清晰,但是有些地方还需要继续优化:
1.代码违背了DRY准则,每个方法内部都会做一次循环
2.对于每种规则都会有一个方法与之对应,每种情况都需要实现一个方法与之对应
3.代码不够灵活,可扩展性不高“
(3)第一次修改
以下代码我们尝试将判断条件抽出来,放到另一个单独的方法中
public class TestLambda1{
public static void main(String\[\] args){
List staffList = StaffUtil.createStaffList(20);
filterA(staffList);
}
public static void filterA(List<Staff> staffList){
for(Staff staff:staffList){
if(isA(staff)){
sendMoney(staff,2000);
}
}
}
public static void filterB(List<Staff> staffList){
for(Staff staff:staffList){
if(isB(staff)){
sendMoney(staff,5000);
}
}
}
public static void filterC(List<Staff> staffList){
for(Staff staff:staffList){
if(isC(staff)){
sendMoney(staff,10000);
}
}
}
public static void filterD(List<Staff> staffList){
for(Staff staff:staffList){
if(isD(staff)){
sendMoney(staff,10000);
}
}
}
public static boolean isA(Staff staff){
return staff.getWorkYears() <= 1;
}
public static boolean isB(Staff staff){
return staff.getWorkYears() <= 3 && staff.getWorkYears() > 1;
}
public static boolean isC(Staff staff){
return staff.getWorkYears() <= 5 && staff.getWorkYears() > 3;
}
public static boolean isD(Staff staff){
return staff.getWorkYears() <= 5 && staff.getWorkYears() > 3;
}
public static void sendMoney(Staff staff,int money){
System.out.println(“发放”+staff.getName()+”奖金”+money+”元”);
}
}
代码将判断条件抽出来放到方法里面是的每个判断条件得以重用,这是相对之前代码一个提高。那么对于判断条件的处理有没有更好的方法呢?
(4)匿名内部类
定义一个判断员工工作年限的接口,如下:
public interface MyTest<T>{
boolean test(T t);
}
public class TestLambda1{
public static void main(String\[\] args){
List staffList = StaffUtil.createStaffList(20);
filterA(staffList,new MyTest<Staff>(){
@Override
boolean test(Staff staff){
return staff.getWorkYears() <= 1;
}
});
filterA(staffList,new MyTest<Staff>(){
@Override
boolean test(Staff staff){
return staff.getWorkYears() <= 3 && staff.getWorkYears() > 1;
}
});
//…
}
public static void filterA(List<Staff> staffList,MyTest<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,2000);
}
}
}
public static void filterB(List<Staff> staffList,MyTest<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,5000);
}
}
}
public static void filterC(List<Staff> staffList,MyTest<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,10000);
}
}
}
public static void filterD(List<Staff> staffList,MyTest<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,10000);
}
}
}
public static void sendMoney(Staff staff,int money){
System.out.println(“发放”+staff.getName()+”奖金”+money+”元”);
}
}
上面的代码使用匿名内部类来代替刚才的方法,好处时,减少了三个方法,而且对于判断规则,没必要写死,只要在调用时给出规则即可。但是缺点是使用匿名内部类,使得代码变得臃肿无比。
(5)使用优雅的Lambda表达式
前面(4)定义了一个MyTest接口,该接口只有一个抽象方法test,结合我们前面讲到的Functional Interface,所以该接口是支持lambda表达式的。其实我们定义这个方法是没有必要的,因为java中已经定义了类似的接口供我们使用:Java.util.function.Predicate
public class TestLambda1{
public static void main(String\[\] args){
List staffList = StaffUtil.createStaffList(20);
Predicate<Staff> preA = staff -> staff.getWorkYears() <= 1;
Predicate<Staff> preB = staff -> staff.getWorkYears() <= 3 && staff.getWorkYears() > 1;
filterA(staffList,preA);
filterA(staffList,preB);
//…
}
public static void filterA(List<Staff> staffList,Predicate<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,2000);
}
}
}
public static void filterB(List<Staff> staffList,Predicate<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,5000);
}
}
}
public static void filterC(List<Staff> staffList,Predicate<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,10000);
}
}
}
public static void filterD(List<Staff> staffList,Predicate<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,10000);
}
}
}
public static void sendMoney(Staff staff,int money){
System.out.println(“发放”+staff.getName()+”奖金”+money+”元”);
}
}
怎么样代码是不是变的优雅许多了呢?