Lambda是一个匿名函数,咱们能够把Lambda表达式理解为是一段能够传递的代码(将代码像数据同样传递)。能够写出更简洁、更灵活的代码。做为一种更紧凑的代码风格,使Java的语言表达能力获得了提高。java
知道了Lambda表达式是什么以后,咱们也没啥感受,下面咱们来体验一下Lambda表达式到底有啥好的。
咱们回顾下咱们原来学过的的匿名内部类算法
//原来的匿名内部类 @Test public void test01(){ //一个Comparator接口比较两个Integer的大小 Comparator<Integer> com = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; //当用匿名内部类实现了一个接口之后咱们就能够把这个匿名内部类的实例做为参数进行传递 TreeSet<Integer> set = new TreeSet(com); }
咱们来看这段代码,最核心关键的一行代码就只有一行设计模式
return Integer.compare(o1,o2);
因此下面就有请咱们的Lambda表达式闪亮登场来解决这个一堆的代码量的问题啦。api
下面咱们用Lambda表达式来简化上面编写的匿名内部类。jvm
//Lambda表达式 @Test public void test02(){ //把上面的匿名内部类核心的一行代码Integer.compare(o1,o2)提取做为一个实现 Comparator<Integer> com = (x,y) -> Integer.compare(x,y); TreeSet<Integer> set = new TreeSet(com); }
是否是以为简单了,若是你还没找到感受,咱们再来看一个例子。ide
假设有这么一个需求,你要获取当前公司中员工年龄大于35的员工信息。函数
咱们通常要怎么实现呢?首先得先有一个员工的实体类Employee测试
package com.cqq.java8; public class Employee { private String name; private int age; private double salary; public Employee(String name, int age, double salary) { this.name = name; this.age = age; this.salary = salary; } public Employee() { } public String getName() { return name; } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}'; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
而后在写一个用于过滤当前公司员工中年龄大于35的员工信息。优化
List<Employee> employees = Arrays.asList( new Employee("张三",68,9000), new Employee("李四",38,8000), new Employee("王五",50,4000), new Employee("赵六",18,3000), new Employee("田七",8,1000)); //获取当前公司中员工年龄大于35的员工信息 public List<Employee> filterEmployee(List<Employee> employees){ List<Employee> emps = new ArrayList<>(); for (Employee emp: employees) { if(emp.getAge() > 35){ emps.add(emp); } } return emps; }
而后写个测试方法ui
@Test public void test03(){ List<Employee> list = filterEmployee(employees); for (Employee employee:list ) { System.out.println(employee); } }
测试结果以下
Employee{name='张三', age=68, salary=9000.0} Employee{name='李四', age=38, salary=8000.0} Employee{name='王五', age=50, salary=4000.0}
咱们获取到了年龄大于35的员工信息。
这时候,若是咱们的需求再增长一个,获取当前公司中员工工资大于5000的员工信息。因而,咱们还要在再写一个用于获取当前公司中员工工资大于5000的员工信息的方法。
//当前公司中员工工资大于5000的员工信息 public List<Employee> filterEmployee2(List<Employee> employees){ List<Employee> emps = new ArrayList<>(); for (Employee emp: employees) { if(emp.getSalary() > 5000){ emps.add(emp); } } return emps; }
而后咱们发现,上面需求的俩方法有大量的冗余代码,除了判断语句以外,其余几乎都是相同的代码,这时候,咱们就想要来优化了。原来咱们对这种冗余代码最好的优化方式就是用设计模式,想一想用什么设计模式呢,这里咱们可使用策略模式。下面咱们来进行优化。
策略模式 :定义一系列算法,把每个算法封装起来,而且使它们可相互替换。简单说就是咱们能够根据环境或者条件的不一样选择不一样的算法或者策略来完成某个功能,再通俗点说就是给他什么策略他就用什么策略来过滤。
这里就是根据判断条件的不一样选出对应的员工Employee。
因为俩方法是条件不一致,咱们能够来定义一个接口来进行条件判断。
//声明一个带泛型的接口 public interface MyPredicate<T> { //声明一个boolean的方法,传进来一个T,而后对T操做,条件判断,返回一个boolean public boolean test(T t); }
而后,若是咱们再有一个新的判断规则,只须要定义一个新的实现类实现MyPredicate接口便可。
下面是对员工年龄大于35的判断的实现类
public class FilterEmployeeByAge implements MyPredicate<Employee> { @Override public boolean test(Employee employee) { return employee.getAge() > 35; } }
这个是员工员工工资大于5000的实现类
public class FilterEmployeeBySalary implements MyPredicate<Employee> { @Override public boolean test(Employee employee) { return employee.getSalary() > 5000; } }
过滤方法以下
//参数传进来一个MyPredicate接口的实现类em public List<Employee> filterEmployee(List<Employee> list, MyPredicate em){ List<Employee> emps = new ArrayList<>(); for (Employee employee: list) { //实现类的判断方法 if(em.test(employee)){ emps.add(employee); } } return emps; }
测试方法
@Test public void test04(){ System.out.println("========年龄大于35========="); //传进来的参数为接口年龄判断的实现类 List<Employee> list = filterEmployee(employees,new FilterEmployeeByAge()); for (Employee employee:list ) { System.out.println(employee); } System.out.println("========工资大于5000========="); //传进来的参数为工资年龄判断的实现类 List<Employee> list2 = filterEmployee(employees,new FilterEmployeeBySalary()); for (Employee employee:list2 ) { System.out.println(employee); } }
测试结果
========年龄大于35========= Employee{name='张三', age=68, salary=9000.0} Employee{name='李四', age=38, salary=8000.0} Employee{name='王五', age=50, salary=4000.0} ========工资大于5000========= Employee{name='张三', age=68, salary=9000.0} Employee{name='李四', age=38, salary=8000.0}
虽说使用了策略模式来重构了代码,可是为每一个判断类都定义一个实现类,仍是不那么好。接下来,咱们直接使用匿名内部类实现接口的方式继续优化代码。
@Test public void test05(){ //传进来的参数为一个匿名内部类,不用每加一个判断条件再加一个接口的实现类了 List<Employee> list = filterEmployee(employees, new MyPredicate<Employee>() { @Override public boolean test(Employee t) { return t.getSalary() < 5000; } }); System.out.println("========工资小于5000========="); for (Employee employee:list ) { System.out.println(employee); } }
测试结果
========工资小于5000========= Employee{name='王五', age=50, salary=4000.0} Employee{name='赵六', age=18, salary=3000.0} Employee{name='田七', age=8, salary=1000.0}
这样是否是好多了,传进来的参数为一个匿名内部类,不用每加一个判断条件再加一个接口的实现类了。再来看看这个方式是否是最好的呢,咱们发现其实匿名内部类里有用的代码仍是就 return t.getSalary() < 5000;这个判断,可读性仍是有点差,咱们是否是还能够再继续优化呢?接下来咱们继续往下看。
@Test public void test06(){ List<Employee> list = filterEmployee(employees, (e) -> e.getSalary() > 5000); list.forEach(System.out::println ); }
优化二里的匿名内部类用了Lambda表达式(e) -> e.getSalary() > 5000 来替代
测试结果
Employee{name='张三', age=68, salary=9000.0} Employee{name='李四', age=38, salary=8000.0}
若是你以为这种方式还不够好,其实还有更好的,咱们往下看
@Test public void test5() { //获取当前公司中员工工资不小于5000的员工信息 employees.stream() .filter((e) -> e.getSalary() >= 5000) .forEach(System.out::println); }
测试结果
Employee{name='张三', age=68, salary=9000.0} Employee{name='李四', age=38, salary=8000.0}
Java8中引入了一个新的操做符"->",叫箭头操做符或者称为Lamnbda操做符
该操做符分为两部分:
左侧:Lambda表达式的参数列表(对应接口中抽象方法的参数列表)
右侧:Lambda表达式中所须要执行的功能,即Lambda体(对应接口中抽象方法的实现)
() -> System.out.println();
这种语法格式说的就是接口中的那个抽象方法无参,而且无返回值。
Lambda表达式须要函数式接口的支持,咱们熟悉的Runnable接口就是函数式接口,而且使无参且无返回值的,Runnable接口的定义以下
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
下面咱们来看一下,原来咱们是怎样经过匿名内部类的方式去实现的接口的呢?
//Lambda表达式须要函数式接口的支持,咱们的Runnable接口就是函数式接口 @Test public void test01(){ Runnable runnable = new Runnable() { @Override public void run() { System.out.println("Hello Runnable"); } }; }
下面咱们经过Lambda表达式的方法实现接口
//经过Lambda表达式实现接口 Runnable runnable1 = () -> System.out.println("Hello Lambda"); runnable1.run();
咱们完整的测试方法以下
//Lambda表达式须要函数式接口的支持,咱们的Runnable接口就是函数式接口 @Test public void test01(){ //经过匿名内部类的方式实现接口 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("Hello Runnable"); } }; runnable.run(); System.out.println("--------------------"); //经过Lambda表达式实现接口 Runnable runnable1 = () -> System.out.println("Hello Lambda"); runnable1.run(); }
运行结果
Hello Runnable -------------------- Hello Lambda
咱们能够看出匿名内部类和Lambda表达式实现接口的效果是同样的,可是Lambda表达式简介了不少。
这里还有一个小点须要注意:若是咱们在局部内部类中,应用了一个同级别的局部变量,在jdk1.8之前该变量必须是final的,1.8开始能够不加final了,底层默认给咱们加上了,不须要咱们手动加了,当咱们对变量进行自增(++)或自减(--)操做时仍是不行的。
@Test public void test01(){ //这里fianl能够不加,jdk1.8开始给咱们默认记上了final,不须要咱们手动加了 /*final*/ int num = 0; //经过匿名内部类的方式实现接口 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("Hello Runnable"+num); } }; runnable.run(); System.out.println("--------------------"); //经过Lambda表达式实现接口 Runnable runnable1 = () -> System.out.println("Hello Lambda"+num); runnable1.run(); }
运行结果num的数也打印出来了
Hello Runnable0 -------------------- Hello Lambda0
这种语法格式说的就是接口中的那个抽象方法有一个参数,而且无返回值。
咱们以Consumer接口为例来讲明,下面是Consumer接口的定义
@FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); /** * Returns a composed {@code Consumer} that performs, in sequence, this * operation followed by the {@code after} operation. If performing either * operation throws an exception, it is relayed to the caller of the * composed operation. If performing this operation throws an exception, * the {@code after} operation will not be performed. * * @param after the operation to perform after this operation * @return a composed {@code Consumer} that performs in sequence this * operation followed by the {@code after} operation * @throws NullPointerException if {@code after} is null */ default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
测试方法
//Lambda表达式 @Test public void test02(){ Consumer consumer = (x) -> System.out.println(x); consumer.accept("==这是测试结果=="); }
运行结果
==这是测试结果==
这个例子说明:Lambda表达式就是对Consumer接口的accept(T t)这个抽象方法的实现,实现的方法体为打印一下输入的参数x。
这种语法格式说的就是接口中的那个抽象方法只有一个参数时,小括号能够写也能够不写。
//Lambda表达式 @Test public void test02(){ //只有一个参数,小括号写 Consumer<String> consumer = (x) -> System.out.println(x); //只有一个参数,小括号不写 Consumer consumer2 = x -> System.out.println(x); consumer.accept("==这是测试结果=="); }
这种语法格式说的就是接口中的那个抽象方法有两个及以上的参数,而且Lambda方法体中有多条语句,此时Lambda体要用大括号{}括起来。
我就以Comparator接口为例来说述这种语法格式。咱们只看Comparator接口的compare(T o1, T o2)方法的定义
@FunctionalInterface public interface Comparator<T> { //--省略 int compare(T o1, T o2); }
测试方法
@Test public void test03(){ Comparator<Integer> comparator = (x,y) -> { //方法体中多条语句 System.out.println("方法体中多条语句1"); System.out.println("方法体中多条语句2"); return Integer.compare(x,y); }; System.out.println(comparator.compare(2,4)); System.out.println(comparator.compare(4,2)); System.out.println(comparator.compare(2,2)); }
测试结果
方法体中多条语句1 方法体中多条语句2 -1 方法体中多条语句1 方法体中多条语句2 1 方法体中多条语句1 方法体中多条语句2 0
这种语法格式说的就是接口中的那个抽象方法有两个及以上的参数,而且Lambda方法体中只有一条语句,此时return和大括号{}均可以不写。
@Test public void test04(){ Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y); System.out.println(comparator.compare(2,4)); }
这种语法格式说的就是Lambda表达式中的参数列表的数据类型能够省略不写,由于JVM的编译器能够经过上下文来推断出数据类型,这个过程咱们称之为"类型推断",其实,“类型推断"也是一个语法糖。
@Test public void test05(){ Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y); //x,y的数据类型Integer能够不写,JVM根据上下文Comparator<Integer>这个泛型的Integer能够推断出来 Comparator<Integer> comparator2 = (Integer x,Integer y) -> Integer.compare(x,y); }
这里x,y的数据类型Integer能够不写,JVM根据上下文Comparator
咱们总结下语法的原则:
上联:左右遇一括号省(左边一个参数,右边一个返回值时,参数的小括号和方法体的大括号均可以省略)
下联:左侧推断类型省(箭头操做符的左侧参数类型能够经过目标上下文推断出来,便可以省略不写)
横批:能省则省