携程四面:说说Lambda表达式的演化过程!

# 前言 **能够使用Lambda的依据是必须有相应的函数接口**(函数接口,是指内部只有一个抽象方法的接口)。 这一点跟Java是强类型语言吻合,_也就是说你并不能在代码的任何地方任性的写Lambda表达式_。实际上Lambda的类型就是对应函数接口的类型。Lambda表达式另一个**依据是类型推断机制(重点)**,在上下文信息足够的情况下,**编译器可以推断出参数表的类型,而不需要显式指名**。 读者福利:[Java核心学习笔记](https://mp.weixin.qq.com/s/AgJ9iymCiM63a9E20mlA6Q)+2021最新[大厂面试真题](https://mp.weixin.qq.com/s/VZ7wormwowlSPnA_XAnZaw)共享! # 一、演化过程 ## A.基础类的准备 ```javascript package com.os.model; import java.util.Objects; public class Employee { private int id; private String name; private int age; private double salary; //省略生成的getteer和setter方法,构造方法、toString方法、hashCode、equals方法 } 复制代码 ``` ## B.筛选数据 我们根据不同的筛选条件需要设置不同的方法,增加了很多的代码量。 ```javascript package com.os.test; import com.os.model.Employee; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Demo01 { private static List emps = Arrays.asList( new Employee(101, "悟空", 18, 9999.99), new Employee(102, "八戒", 59, 6666.66), new Employee(103, "唐僧", 28, 3333.33), new Employee(104, "沙僧", 8, 7777.77), new Employee(105, "白龙马", 38, 5555.55) ); public static void main(String[] args) { System.out.println("===>1.筛选年龄"); List list = filterEmployeeAge(emps); for (Employee employee : list) { System.out.println(employee); } System.out.println("===>2.筛选工资"); list = filterEmployeeSalary(emps); for (Employee employee : list) { System.out.println(employee); } } /** * 需求:获取公司中年龄小于 35 的员工信息 * @param employeeList * @return */ public static List filterEmployeeAge(List employeeList){ List list = new ArrayList<>(); for (Employee emp : employeeList) { if(emp.getAge() <= 35){ list.add(emp); } } return list; } /** * 需求:获取公司中工资大于 5000 的员工信息 * @param employeeList * @return */ public static List filterEmployeeSalary(List employeeList){ List list = new ArrayList<>(); for (Employee emp : employeeList) { if(emp.getSalary() >= 5000){ list.add(emp); } } return list; } } 复制代码 ``` ## C.代码进化:策略设计模式 ### **(1)定义筛选条件的泛型接口** ```javascript package com.os.service; /** * 针对于数据的筛选条件的接口 */ public interface ObjectDataPredicate { boolean test(T t); } 复制代码 ``` ### **(2)实现类去实现不同的筛选方式** 按照年龄进行筛选实现类 ```javascript package com.os.service; import com.os.model.Employee; public class FilterEmployeeForAge implements ObjectDataPredicate { @Override public boolean test(Employee employee) { return employee.getAge() <= 35; } } 复制代码 ``` 按照工资进行筛选实现类 ```javascript package com.os.service; import com.os.model.Employee; public class FilterEmployeeForSalary implements ObjectDataPredicate { @Override public boolean test(Employee employee) { return employee.getSalary() >= 5000; } } 复制代码 ``` ### **(3)策略模式的实现代码** ```javascript public static List filterEmployee(List emps, ObjectDataPredicate objectDataPredicate){ List list = new ArrayList<>(); for (Employee employee : emps) { if(objectDataPredicate.test(employee)){ list.add(employee); } } return list; } 复制代码 ``` 完整代码如下 ```javascript package com.os.test; import com.os.model.Employee; import com.os.service.FilterEmployeeForAge; import com.os.service.FilterEmployeeForSalary; import com.os.service.ObjectDataPredicate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Demo02 { private static List emps = Arrays.asList( new Employee(101, "悟空", 18, 9999.99), new Employee(102, "八戒", 59, 6666.66), new Employee(103, "唐僧", 28, 3333.33), new Employee(104, "沙僧", 8, 7777.77), new Employee(105, "白龙马", 38, 5555.55) ); public static void main(String[] args) { List list = filterEmployee(emps, new FilterEmployeeForAge());//接口回调 for (Employee employee : list) { System.out.println(employee); } System.out.println("------------------------------------------"); List list2 = filterEmployee(emps, new FilterEmployeeForSalary());//接口回调 for (Employee employee : list2) { System.out.println(employee); } } public static List filterEmployee(List emps, ObjectDataPredicate objectDataPredicate){ List list = new ArrayList<>(); for (Employee employee : emps) { if(objectDataPredicate.test(employee)){ list.add(employee); } } return list; } } 复制代码 ``` > 这种代码的实现类太多了 > ## D.代码进化:匿名内部类 ```javascript package com.os.test; import com.os.model.Employee; import com.os.service.FilterEmployeeForAge; import com.os.service.FilterEmployeeForSalary; import com.os.service.ObjectDataPredicate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Demo03 { private static List emps = Arrays.asList( new Employee(101, "悟空", 18, 9999.99), new Employee(102, "八戒", 59, 6666.66), new Employee(103, "唐僧", 28, 3333.33), new Employee(104, "沙僧", 8, 7777.77), new Employee(105, "白龙马", 38, 5555.55) ); public static void main(String[] args) { List list = filterEmployee(emps, new ObjectDataPredicate() { @Override public boolean test(Employee employee) { return employee.getId() <= 103; } });//接口回调 for (Employee employee : list) { System.out.println(employee); } } public static List filterEmployee(List emps, ObjectDataPredicate objectDataPredicate){ List list = new ArrayList<>(); for (Employee employee : emps) { if(objectDataPredicate.test(employee)){ list.add(employee); } } return list; } } 复制代码 ``` > 通过内部类,我们能发现整个代码中核心的部分就是一句话`employee.getId() <= 103` > ## E.代码进化:Lambda 表达式 ```javascript package com.os.test; import com.os.model.Employee; import com.os.service.ObjectDataPredicate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Demo04 { private static List emps = Arrays.asList( new Employee(101, "悟空", 18, 9999.99), new Employee(102, "八戒", 59, 6666.66), new Employee(103, "唐僧", 28, 3333.33), new Employee(104, "沙僧", 8, 7777.77), new Employee(105, "白龙马", 38, 5555.55) ); public static void main(String[] args) { /* List list = filterEmployee(emps, new ObjectDataPredicate() { @Override public boolean test(Employee employee) { return employee.getId() <= 103; } });//接口回调 */ List list = filterEmployee(emps, (e) -> e.getAge() <= 35); for (Employee employee : list) { System.out.println(employee); } System.out.println("------------------------------------------"); List list2 = filterEmployee(emps, (e) -> e.getSalary() >= 5000); for (Employee employee : list2) { System.out.println(employee); } } public static List filterEmployee(List emps, ObjectDataPredicate objectDataPredicate){ List list = new ArrayList<>(); for (Employee employee : emps) { if(objectDataPredicate.test(employee)){ list.add(employee); } } return list; } } 复制代码 ``` > Lambda 是一个匿名函数,我们可以把Lambda 表达式理解为是**一段可以传递的代码(将代码像数据一样进行传递)**。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。 > # 二、Lambda基础语法 Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为`->`,该操作符被称为Lambda 操作符或剪头操作符。它将Lambda 分为两个部分: - 左侧:指定了Lambda 表达式需要的所有参数(对应接口中形参) - 右侧:指定了Lambda 体,即Lambda 表达式要执行的功能。(方法体,可以推断返回值类型) ## A.格式1:无参数,无返回值 ```javascript package com.os.print.service; @FunctionalInterface//定义接口函数 public interface Printer01 { void print(); } 复制代码 ``` ```javascript package com.os.print.service; public class Demo01 { public static void main(String[] args) { //之前我们可以使用你们实现类 Printer01 out = new Printer01() { @Override public void print() { System.out.println("匿名实现类"); System.out.println("====>"+Math.random()); } }; out.print(); //使用Lambda表达式 out = ()-> System.out.println("方法体只有一行,可以省略大括号"); out.print(); out = ()->{ System.out.println("方法体有很多,需要使用大括号搞定"); System.out.println("====>"+Math.random()); }; out.print(); } } 复制代码 ``` ## B.格式2:有一个参数,无返回值 ```javascript package com.os.print.service; @FunctionalInterface//定义接口函数 public interface Printer02 { void print(T t); } 复制代码 ``` ```javascript public class Demo02 { public static void main(String[] args) { //通过泛型推断参数e的类型 Printer02 out01 = (e)-> System.out.println(e); out01.print(new Employee(999,"悟空",19,25000)); Printer02 out2 = (e)-> System.out.println(e); out2.print(999); Printer02 out3 = (e)-> System.out.println(e); out3.print("西游记"); } } 复制代码 ``` ## C.格式3:若只有一个参数,小括号可以省略不写 ```javascript package com.os.print.service; import com.os.model.Employee; public class Demo02 { public static void main(String[] args) { //通过泛型推断参数e的类型 Printer02 out01 = e-> System.out.println(e); out01.print(new Employee(999,"悟空",19,25000)); Printer02 out2 = e-> System.out.println(e); out2.print(999); Printer02 out3 = e-> System.out.println(e); out3.print("西游记"); } } 复制代码 ``` ## D.格式4:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句 使用系统有的函数接口测试如下: ```javascript package com.os.print.service; import com.os.model.Employee; import java.util.Comparator; public class Demo03 { public static void main(String[] args) { /* public interface Comparator { } * */ Comparator comparator = (x,y)->{ System.out.println("接口函数方法"); return Integer.compare(x,y); }; } } 复制代码 ``` ### 自定义函数接口方法: ```javascript package com.os.print.service; @FunctionalInterface//定义接口函数 public interface Printer03 { T print(T t1,T t2); } 复制代码 ``` ```javascript package com.os.print.service; import com.os.model.Employee; import java.util.Comparator; public class Demo03 { public static void main(String[] args) { Printer03 out01 = (s1,s2)->{ String str = s1.concat(s2); return str.toUpperCase(); }; System.out.println(out01.print("abc","efg")); } } 复制代码 ``` ### 自定义函数接口方法两个参数: ```javascript package com.os.print.service; @FunctionalInterface//定义接口函数 public interface Printer04<T,R> { R print(T t1, R t2); } 复制代码 ``` ```javascript package com.os.print.service; import com.os.model.Employee; public class Demo04 { public static void main(String[] args) { Printer04<String, Employee> out = (name,e)->{ e.setName(name); return e; }; Employee employee = out.print("西游记",new Employee()); System.out.println(employee); } } 复制代码 ``` ## E.格式5:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写 ```javascript package com.os.print.service; import com.os.model.Employee; import java.util.Comparator; public class Demo04 { public static void main(String[] args) { Comparator comparator = (x, y)->Integer.compare(x,y); System.out.println(comparator.compare(1,2)); Printer04<String, Employee> out = (name,e)->e; Employee employee = out.print("西游记",new Employee()); System.out.println(employee); } } 复制代码 ``` ## F.格式5:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断” ```javascript (Integer x, Integer y) -> Integer.compare(x, y); //一般不会使用这种写法 复制代码 ``` 上述Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为javac根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断” > lambda语法的总结如下: > - **上联:左右遇一括号省** - **下联:左侧推断类型省** - **横批:能省则省** # 三、函数式接口 - 只包含一个抽象方法的接口,称为函数式接口。 - 你可以通过Lambda 表达式来创建该接口的对象。 - (若Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。 - 在任意函数式接口上设置`@FunctionalInterface`注解,这样做可以检查它是否是一个函数式接口,同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。 上述的示例中,我们已经定义过函数式接口,但是我们不可能每次都要自己定义函数式接口,太麻烦!所以,Java内置了函数式接口在`java.util.function`包下 ## A.Predicate 断言型接口 ```javascript @FunctionalInterface public interface Predicate { boolean test(T t); } 复制代码 ``` ```javascript package com.os.print.service; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Predicate; public class Demo05 { public static void main(String[] args) { List list = Arrays.asList("Hello", "pangsir", "Lambda", "www", "ok"); List strList = filterStr(list, (s) -> s.length() > 3); for (String str : strList) { System.out.println(str); } } //需求:将满足条件的字符串,放入集合中 public static List filterStr(List list, Predicate pre){ List strList = new ArrayList<>(); for (String str : list) { if(pre.test(str)){ strList.add(str); } } return strList; } } 复制代码 ``` ## B.Function<T,R> 函数型接口 ```javascript @FunctionalInterface public interface Function<T, R> { R apply(T t); } 复制代码 ``` ```javascript package com.os.print.service; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; public class Demo06 { public static void main(String[] args) { String newStr = strHandler("\t\t\t 西游记齐天大圣孙悟空 ", (str) -> str.trim()); System.out.println(newStr); String subStr = strHandler("西游记齐天大圣孙悟空", (str) -> str.substring(2, 5)); System.out.println(subStr); } //需求:用于处理字符串 public static String strHandler(String str, Function<String, String> fun){ return fun.apply(str); } } 复制代码 ``` ## C.Supplier 供给型接口 ```javascript @FunctionalInterface public interface Supplier { T get(); } 复制代码 ``` ```javascript package com.os.print.service; import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; public class Demo07 { public static void main(String[] args) { List numList = getNumList(10, () -> (int)(Math.random() * 100)); for (Integer num : numList) { System.out.println(num); } } //需求:产生指定个数的整数,并放入集合中 public static List getNumList(int num, Supplier sup){ List list = new ArrayList<>(); for (int i = 0; i < num; i++) { Integer n = sup.get(); list.add(n); } return list; } } 复制代码 ``` ## D.Consumer 消费型接口 ```javascript @FunctionalInterface public interface Consumer { void accept(T t); } 复制代码 ``` ```javascript package com.os.print.service; import java.util.function.Consumer; public class Demo08 { public static void main(String[] args) { happy(10000, (m) -> System.out.println("购物消费:" + m + "元")); } public static void happy(double money, Consumer con){ con.accept(money); } } 复制代码 ``` ### java.util.function包下有很多有用的函数式接口 | 函数式接口 | 参数类型 | 返回类型 | 用途 | |:----|:----|:----|:----| | Consumer消费型接口 | T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) | | Supplier供给型接口 | 无 | T | 返回类型为T的对象,包含方法:T get(); | | Function<T, R>函数型接口 | T | R | 对类型为T的对象应用操作。结果R类型的对象。方法:R apply(T t); | | Predicate断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,boolean 值。含方法boolean test(T t); | | BiFunction<T,U,R> | T,U | R | 对类型为T,U参数应用操作,返回R类型的结果。包含方法为:R apply(T t,U u); | | UnaryOperator | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为T apply(Tt); | | BinaryOperator | T,T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为T apply(Tt1,Tt2); | | BiConsumer<T,U> | T,U | void | 对类型为T,U参数应用操作。包含方法为void accept(T t,U u) | | ToIntFunctionToLongFunctionToDoubleFunction | T | intlongdouble | 分别计算int、long、double、值的函数 | | IntFunctionLongFunctionDoubleFunction | intlongdouble | R | 参数分别为int、long、double类型的函数 | # 四、方法引用 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用! 方法引用:使用操作符“::” 将方法名和对象或类的名字分隔开来。 - 对象::实例方法 - 类::静态方法 - 类::实例方法 > 注意: > - ① 方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致! 复制代码 - ② 若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName 复制代码 ## A.对象的引用 :: 实例方法名 ```javascript package com.os.print.service; import com.os.model.Employee; import java.util.function.Supplier; public class Demo09 { public static void main(String[] args) { Employee emp = new Employee(101, "张三", 18, 9999.99); Supplier sup = () -> emp.getName(); System.out.println(sup.get()); System.out.println("----------------------------------"); Supplier sup2 = emp::getName; System.out.println(sup2.get()); } } 复制代码 ``` ## B.类 :: 静态方法名 ```javascript package com.os.print.service; import java.util.function.BiFunction; import java.util.function.Supplier; public class Demo10 { public static void main(String[] args) { BiFunction<Double, Double, Double> fun = (x, y) -> Math.max(x, y); System.out.println(fun.apply(1.5, 22.2)); System.out.println("--------------------------------------------------"); BiFunction<Double, Double, Double> fun2 = Math::max; System.out.println(fun2.apply(1.2, 1.5)); } } 复制代码 ``` ## C.类 :: 实例方法名 ```javascript package com.os.print.service; import com.os.model.Employee; import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.Function; public class Demo11 { public static void main(String[] args) { BiPredicate<String, String> bp = (x, y) -> x.equals(y); System.out.println(bp.test("abcde", "abcde")); System.out.println("-----------------------------------------"); BiPredicate<String, String> bp2 = String::equals; System.out.println(bp2.test("abc", "abc")); System.out.println("-----------------------------------------"); Function<Employee, String> fun = (e) -> e.getName(); System.out.println(fun.apply(new Employee())); System.out.println("-----------------------------------------"); Function<Employee, String> fun2 = Employee::getName; System.out.println(fun2.apply(new Employee())); } } 复制代码 ``` > 若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName > ## D.构造方法引用 ClassName::new 格式:`ClassName::new` 与函数式接口相结合,自动与函数式接口中方法兼容。 可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致! ```javascript package com.os.print.service; import com.os.model.Employee; import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.Function; public class Demo12 { public static void main(String[] args) { Function<String, Employee> fun = Employee::new; System.out.println(fun.apply("悟空")); BiFunction<String, Integer, Employee> fun2 = Employee::new; System.out.println(fun2.apply("八戒",18)); } } 复制代码 ``` ## E.数组引用 ```javascript package com.os.print.service; import com.os.model.Employee; import java.util.function.BiFunction; import java.util.function.Function; public class Demo13 { public static void main(String[] args) { Function<Integer, String[]> fun = (e) -> new String[e]; String[] strs = fun.apply(10); System.out.println(strs.length); System.out.println("--------------------------"); Function<Integer, Employee[]> fun2 = Employee[] :: new; Employee[] emps = fun2.apply(20); System.out.println(emps.length); } } 关注公众号:麒麟改bug 共享2021金三银四Java面试题总结集锦! ```
上一篇:MySQL 学习笔记十一——数据分组


下一篇:10.力扣-树-员工的重要性