百度说 “Lambda 表达式”是一个复匿名函数,它可以包含表达式和语句,制并且可用于创建委托或表达式树类型。
需求:获取当前公司中员工年龄大于35岁的员工信息
List<Employee> employees = Arrays.asList(
//id、name、age、salary
new Employee(1, "张三", 35, 9999.9),
new Employee(2, "李四", 18, 18888.0),
new Employee(3, "王五", 44, 14444.0),
new Employee(4, "赵六", 23, 2333.0),
new Employee(5, "钱七", 47, 14244.0),
new Employee(6, "陈八", 34, 7444.0),
new Employee(7, "曹九", 39, 944.0)
);
一般的做法是,直接写个过滤方法 如下
@Test
public void test3() {
List<Employee> filter = filterEmployee(employees);
for (Employee employee : filter) {
System.out.println(employee);
}
}
public List<Employee> filterEmployee(List<Employee> list) {
List<Employee> emps = new ArrayList<>();
for (Employee emp : list) {
if (emp.getAge() > 35) {
emps.add(emp);
}
}
return emps;
}
但是如果又增加了需求,又要重新写一个方法,然后直接cope上面的只改一句话,这样代码冗长,繁琐
需求:获取当前公司中员工工资大于5000的员工信息
public List<Employee> filterEmployee2(List<Employee> list) {
List<Employee> emps = new ArrayList<>();
for (Employee emp : list) {
//只需改变一句话,其他完全一样,代码冗长
if (emp.getSalary() > 5000.0) {
emps.add(emp);
}
}
return emps;
}
新建一个接口,和一个实现该接口的类
public interface MyPredicate<T> {
public boolean test(T t);
}
--------------分割线----------------
public class FilterEmpByAge implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getAge()>35;
}
}
然后新建方法,如果mp.test(emp)为true,则执行emp添加到新数组,完成过滤
@Test
public void test4(){
MyPredicate<Employee> mp = new FilterEmpByAge();
List<Employee> employees = filterEmployee(this.employees, mp);
for (Employee employee : employees) {
System.out.println(employee);
}
}
public List<Employee> filterEmployee(List<Employee> list, MyPredicate<Employee> mp) {
List<Employee> emps = new ArrayList<>();
for (Employee emp : list) {
if (mp.test(emp)) {
emps.add(emp);
}
}
return emps;
}
如果出现了新需求,只需要再写一个实现此接口的类,多多少少还是有点繁琐
不用写一个实现此接口的类,直接通过匿名内部类的方式实现
@Test
public void test6(){
MyPredicate<Employee> mp = new MyPredicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getSalary()>=9797;
}
};
List<Employee> employees = filterEmployee(this.employees, mp);
for (Employee employee : employees) {
System.out.println(employee);
}
}
实现匿名内部类多多少少还是有点繁琐,毕竟有效代码就一句话
return employee.getSalary()>=9797;
使用lambda表达式如下,做到了正在的简洁,只需要把最关键的东西写出来就行了
@Test
public void test7(){
List<Employee> list = filterEmployee(employees, (e) -> e.getSalary() <= 5000);
for (Employee employee : employees) {
System.out.println(employee);
}
}
在这之后还有一种方式,那就是Stream API
这个极简,连filterEmployee方法都不用写就完成了过滤
@Test
public void test8(){
employees.stream()
.filter(e -> e.getSalary() >= 5000) //过滤
.limit(1) //限制1个
.forEach(System.out::println); //遍历
}
体验到了Lambda表达式的便捷后,就来学习它的基本语法吧~
一、Lambda 表达式的基础语法:
"->" 箭头操作符 或 Lambda 操作符
左侧:Lambda 表达式的参数列表
右侧:Lambda 表达式所需执行的功能,即Lambda体
语法格式一:无参数,无返回值
() -> System.out.println("Hello World");
语法格式二:有一个参数,并且无返回值
(e) -> System.out.println(e);
小括号可以不写
e -> System.out.println(e);
语法格式三:有两个以上的参数,有返回值,并且Lambda 体中有多条语句
如果只有一条语句,return和{}都可以省略不写(通用)
语法格式四:Lambda 表达式的参数类型可以省略不写
因为JVM编译器可以通过上下文推断出参数数据类型,叫做”类型推断“
上联:左右遇一括号省
下联:左侧推断类型省
横批:能省就省
二、Lambda 表达式需要”函数式接口“的支持
函数式接口:接口中只有一个抽象方法的接口,成为函数式接口
可以在接口上使用注解@FunctionalInterface 修饰,检查接口是否为函数式接口
Consumer :消费型接口
void accept(T t);
Supplier :供给型接口
T get();
Function
R apply(T t);
Predicate :断言型接口
boolean test(T t);
方法引用主要有三种语法格式:
对象::实例方法名
类::静态方法名
类::实例方法名
注意:Lambda 体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
/**
* 例子1
*/
@Test
public void test1(){
//正常的Lambda表达式
Consumer<String> con = (x) -> System.out.println(x);
//方法引用
//println为out对象的一个实例方法
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
Consumer<String> con3 = System.out::println;
}
/**
* 例子2
*/
@Test
public void test2(){
Employee emp = new Employee(1,"niu",33,49999.0);
//正常的Lambda表达式
Supplier<String> sup = () -> emp.getName();
System.out.println(sup.get());
//方法引用
//getSalary为emp对象的一个实例方法
Supplier<Double> sup2 = emp::getSalary;
System.out.println(sup2. get());
}
@Test
public void test3(){
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
//compare是Integer类的静态方法
Comparator<Integer> com2 = Integer::compare;
}
这个要注意,只有当第一个参数是实例方法的调用者,第二个参数是实例方法的参数时 才能这样使用
/**
* 类::实例方法名
*
* 第一个参数是实例方法的调用者,第二个参数是实例方法的参数时 即可这样使用
*/
@Test
public void test4(){
BiPredicate<String,String> bp = (x,y) -> x.equals(y);
//x是equals方法调用者,y为equals方法参数
BiPredicate<String,String> bp2 = String::equals;
}
如果接口中方法有参数,则调用的构造器参数和接口方法中的参数一致
如果没有参数,则调用的无参构造器
/**
* 构造器引用
*
* 如果接口中方法有参数,则调用的构造器参数和接口方法中的参数一致
* 如果没有参数,则调用的无参构造器
*
* 例子1
*/
@Test
public void test5(){
//接口的方法中无参数,调用的无参构造器
Supplier<Employee> sup = ()-> new Employee();
Supplier<Employee> sup1 = Employee::new;
}
/**
* 例子2
*/
@Test
public void test6(){
//接口的方法中有一个参数,调用的构造器也有一个参数
Function<Integer,Employee> function = Employee::new;
Employee employee = function.apply(1);
System.out.println(employee);
//结果 Employee{id=1, name='null', age=null, salary=null}
}
/**
* 数组引用
*/
@Test
public void test7(){
Function<Integer,String[]> fun = (x) -> new String[x];
Function<Integer,String[]> fun2 = String[]::new;
String[] apply = fun2.apply(2);
}
学习使我快乐