1、Lambda表达式简介
Lambda 表达式,也称为闭包,是 Java 8 中最大和最令人期待的语言改变。它允许将函数当成参数传递给某个方法,或者把代码本身当作数据处理,函数式开发者非常熟悉这些概念。
很多JVM平台上的语言(Groovy、Scala等)从诞生之日就支持 Lambda 表达式,但是 Java 开发者没有选择,只能使用匿名内部类代替Lambda表达式。
//匿名内部类方式排序List<String> names = Arrays.asList( "a", "b", "d" );Collections.sort(names, new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {return s1.compareTo(s2);}});
Lambda 的设计可谓耗费了很多时间和很大的社区力量,最终找到一种折中的实现方案,可以实现简洁而紧凑的语言结构。
Lambda 表达式的语法格式:
(parameters) -> expression或(parameters) ->{ statements; }
Lambda 编程风格,可以总结为四类:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值
2、Lambda表达式的编程风格
2.1 可选类型声明
在使用过程中,可以不用显示声明参数类型,编译器可以统一识别参数类型,例如:
上面代码中的参数Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
s1、s2的类型是由编译器推理得出的,也可以显式指定该参数的类型,例如:
运行之后,两者结果一致!Collections.sort(names, (String s1, String s2) -> s1.compareTo(s2));
2.2 可选的参数圆括号
当方法那只有一个参数时,无需定义圆括号,例如:
但多个参数时,需要定义圆括号,例如:Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
2.3 可选的大括号
当主体只包含了一行时,无需使用大括号,例如:
当主体包含多行时,需要使用大括号,例如:Arrays.asList( "a", "b", "c" ).forEach( e -> System.out.println( e ) );
Arrays.asList( "a", "b", "c" ).forEach( e -> {System.out.println( e );System.out.println( e );} );
2.4 可选的返回关键字
如果表达式中的语句块只有一行,则可以不用使用return语句,返回值的类型也由编译器推理得出,例如:
如果语句块有多行,可以在大括号中指明表达式返回值,例如:Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {int result = e1.compareTo( e2 );return result;} );
2.5 变量作用域
还有一点需要了解的是,Lambda 表达式可以引用类成员和局部变量,但是会将这些变量隐式得转换成final,例如:
和String separator = ",";Arrays.asList( "a", "b", "c" ).forEach(( String e ) -> System.out.print( e + separator ) );
两者等价!final String separator = ",";Arrays.asList( "a", "b", "c" ).forEach(( String e ) -> System.out.print( e + separator ) );
同时,Lambda 表达式的局部变量可以不用声明为final,但是必须不可被后面的代码修改(即隐性的具有final的语义),例如:
在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量,例如:int num = 1;Arrays.asList(1,2,3,4).forEach(e -> System.out.println(num + e));num =2;//报错信息:Local variable num defined in an enclosing scope must be final or effectively final
int num = 1;Arrays.asList(1,2,3,4).forEach(num -> System.out.println(num));//报错信息:Variable 'num' is already defined in the scope
3、与Lambda表达式处理的对比案例
A.匿名内部类和Lambda表达式比较
```java // 原来的匿名内部类 @Test public void comparatorTest() { Comparatorcomparator = new Comparator () {
}; TreeSet@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1, o2);}
treeSet = new TreeSet<>(comparator); }
// Lambda表达式的写法
@Test
public void lambdaTest() {
Comparator
<a name="q5Vhk"></a>## B.需求:对员工进行条件查询<a name="MLDuT"></a>### Employee.java```javapackage com.fcant.java8.lambda.bean;/*** Employee* <p>* encoding:UTF-8** @author Fcant 下午 21:20:09 2020/2/18/0018*/public class Employee {private String name;private int age;private double salary;public Employee() {}public Employee(String name, int age, double salary) {this.name = name;this.age = age;this.salary = salary;}public String getName() {return name;}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;}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", age=" + age +", salary=" + salary +'}';}}
需求方法代码
List<Employee> employees = Arrays.asList(new Employee("Fcant", 14, 99998.0),new Employee("Fcary", 10, 998.045),new Employee("Fcloi", 15, 934598.0),new Employee("Fcmop", 19, 56998.04),new Employee("Fcctr", 18, 945698.0),new Employee("Fcqyt", 17, 998.0645));// 需求:获取当前公司中员工年龄大于17的员工的信息public List<Employee> filterEmployeeByAge(List<Employee> list) {List<Employee> employees = new ArrayList<>();for (Employee employee : list) {if (employee.getAge() > 16) {employees.add(employee);}}return employees;}// 需求:获取当前公司中员工薪水大于5000的员工的信息public List<Employee> filterEmployeeBySalary(List<Employee> list) {List<Employee> employees = new ArrayList<>();for (Employee employee : list) {if (employee.getSalary() > 5000) {employees.add(employee);}}return employees;}
对以上重复性需求进行优化处理
①、优化方式一:策略设计模式
将相差不大需求使用的统一接口来处理
package com.fcant.java8.lambda.inter;/*** ExPredicate* <p>* encoding:UTF-8** @author Fcant 下午 21:40:31 2020/2/18/0018*/public interface ExPredicate<T> {public boolean is(T t);}
对接口进行不同的需求通过实现类来实现
package com.fcant.java8.lambda.inter.impl;import com.fcant.java8.lambda.bean.Employee;import com.fcant.java8.lambda.inter.ExPredicate;/*** FilterEmployeeByAge* <p>* encoding:UTF-8** @author Fcant 上午 10:28:35 2020/2/19/0019*/public class FilterEmployeeByAge implements ExPredicate<Employee> {@Overridepublic boolean is(Employee employee) {return employee.getAge() > 15;}}
package com.fcant.java8.lambda.inter.impl;import com.fcant.java8.lambda.bean.Employee;import com.fcant.java8.lambda.inter.ExPredicate;/*** FilterEmployeeBySalary* <p>* encoding:UTF-8** @author Fcant 上午 10:39:30 2020/2/19/0019*/public class FilterEmployeeBySalary implements ExPredicate<Employee> {@Overridepublic boolean is(Employee employee) {return employee.getSalary() > 5000;}}
// 优化方式一:策略设计模式public List<Employee> filterEmployee(List<Employee> list, ExPredicate<Employee> exPredicate) {List<Employee> employees = new ArrayList<>();for (Employee employee : list) {if (exPredicate.is(employee)) {employees.add(employee);}}return employees;}@Testpublic void Test() {// List<Employee> employees = filterEmployeeByAge(this.employees);List<Employee> employeeListByAge = filterEmployee(this.employees, new FilterEmployeeByAge());for (Employee employee : employeeListByAge) {System.out.println(employee);}System.out.println("----------------根据薪资过滤---------------");List<Employee> employeeListBySalary = filterEmployee(this.employees, new FilterEmployeeBySalary());for (Employee employee : employeeListBySalary) {System.out.println(employee);}}
②、优化方式二:匿名内部类
@Testpublic void Test() {// 优化方式二:匿名内部类System.out.println("-------------匿名内部类-------------");List<Employee> filterEmployee = filterEmployee(this.employees, new ExPredicate<Employee>() {@Overridepublic boolean is(Employee employee) {return employee.getSalary() < 5000;}});for (Employee employee : filterEmployee) {System.out.println(employee);}}
③、优化方式三:Lambda表达式
@Testpublic void Test() {// 优化方式三:Lambda表达式System.out.println("-------------Lambda表达式------------");List<Employee> employeesByLambda = filterEmployee(this.employees, (e) -> e.getSalary() <= 5000);employeesByLambda.forEach(System.out::println);}
④、优化方式四:Stream流
@Testpublic void Test() {// 优化方式四:Stream流System.out.println("-----------------Stream流过滤---------------");employees.stream().filter((e) -> e.getSalary() >= 5000).limit(2).forEach(System.out::println);employees.stream().map(Employee::getName).forEach(System.out::println);}
4、Lambda表达式的基础语法
Java8中引入了一个新的操作符”->”该操作符称为箭头操作符或者Lambda操作符,箭头操作符将Lambda表达式拆分成两部分
- 1.左侧:Lambda表达式的参数列表
- 2.右侧:Lambda表达式中所需执行的功能,即Lambda体
A.语法格式一:无参数,无返回值
() -> System.out.println("Hello Lambda!")
// 1.无参数,无返回值@Testpublic void noParamNoReturnTest() {Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("Hello World");}};runnable.run();System.out.println("---------Lambda实现--------");Runnable r = () -> System.out.println("Hello Lambda!");r.run();}
B.语法格式二:有一个参数,无返回值
(x) -> System.out.println(x);
// 2.有一个参数,无返回值@Testpublic void oneParamNoReturnTest() {Consumer<String> consumer = (x) -> System.out.println(x);consumer.accept("Fv");}
C.语法格式三:有一个参数,无返回值时小括号可以省略不写
x -> System.out.println(x);
// 3.有一个参数,无返回值时可以省略括号@Testpublic void oneParamNoReturnLostTest() {Consumer<String> consumer = x -> System.out.println(x);consumer.accept("Fv");}
D.语法格式四:有一个或多个参数,有返回值,有多条处理语句
Comparator<Integer> comparator = (x, y) -> {System.out.println("函数式接口");return Integer.compare(x, y);};
// 4.有一个或多个参数,有返回值,有多条处理语句@Testpublic void paramReturnTest() {Comparator<Integer> comparator = (x, y) -> {return Integer.compare(x, y);};System.out.println(comparator.compare(1, 12));}
E.语法格式五:若Lambda体中只有一条语句,return和大括号都可以不写
(x, y) -> Integer.compare(x, y);
// 5.只有一条语句,return和大括号都可以不写@Testpublic void paramReturnLostTest() {Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);System.out.println(comparator.compare(1, 12));}
F.语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
(Integer x, Integer y) -> Integer.compare(x, y);
// 6.Lambda表达式的参数列表的数据类型可以省略不写@Testpublic void paramTypeReturnLostTest() {Comparator<Integer> comparator = (Integer x, Integer y) -> Integer.compare(x, y);System.out.println(comparator.compare(1, 12));}
5、Lambda案例
案例A
调用Collection.sort()方法,通过定制排序比较两个Employee(先按年龄比,年龄相同按姓名比,使用Lambda作为参数传递)@Testpublic void empTest() {Collections.sort(employees, (e1, e2) -> {if (e1.getAge() == e2.getAge()) {return e1.getName().compareTo(e2.getName());}else {return -Integer.compare(e1.getAge(), e2.getAge());}});employees.forEach(System.out::println);}
案例B
①声明函数式接口,接口中声明抽象方法,public String getValue(String str);
②声明类TestLambda,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值。
③再将一个字符串的第二个和第四个索引位置进行截取子串 ```java package com.fcant.java8.lambda.inter;
/**
- Func
- encoding:UTF-8 *
- @author Fcant 下午 20:24:19 2020/2/19/0019
*/
@FunctionalInterface
public interface Func
{ String getValue(T t); } java // 案例二 // ①声明函数式接口,接口中声明抽象方法,public String getValue(String str); // ②声明类TestLambda,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值。 // ③再将一个字符串的第二个和第四个索引位置进行截取子串 @Test public void functionTest() { System.out.println(option(“agHdfC”, (x) -> x.toUpperCase())); System.out.println(option(“hsafdsfh”, (x) -> x.substring(2, 5))); }
public String option(String s, Func
<a name="vHNjA"></a>## 案例C①声明一个带两个泛型的函数式接口,泛型类型为`<T, R>`,`T`为参数,`R`为返回值<br />②接口中声明对应的抽象方法<br />③在TestLambda类中声明方法,使用接口作为参数,计算两个long型参数的和<br />④再计算两个long型参数的乘积```javapackage com.fcant.java8.lambda.inter;/*** Funca* <p>* encoding:UTF-8** @author Fcant 下午 20:32:15 2020/2/19/0019*/@FunctionalInterfacepublic interface Funca<T, R> {R op(T t1, T t2);}
// 案例三// ①声明一个带两个泛型的函数式接口,泛型类型为<T, R>,T为参数,R为返回值// ②接口中声明对应的抽象方法// ③在TestLambda类中声明方法,使用接口作为参数,计算两个long型参数的和// ④再计算两个long型参数的乘积@Testpublic void trTest() {System.out.println(ops(12l, 56l, (t1, t2) -> t1 + t2));System.out.println(ops(12l, 56l, (t1, t2) -> t1 * t2));}public Long ops(Long t1, Long t2, Funca<Long, Long> funca) {return funca.op(t1, t2);}
