1、Lambda表达式介绍
Lambda表达式是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样传输),这样就可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java语言表达能力得到提升。
2、Lambda表达式入门示例
需求:获取公司中员工年龄大于35岁的员工信息。
员工类:Employee.java
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public Employee() {}
public Employee(int id) {
this.id = id;
}
public Employee(int id, int age) {
this.id = id;
this.age = age;
}
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
// setter、getter方法……
// toString方法……
}
需求实现:
实现方式一:循环遍历&判断
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;
import org.junit.Test;
public class TestLambda {
List<Employee> employees = Arrays.asList(new Employee("张三", 18, 6666.66), new Employee("李四", 39, 7777.77),
new Employee("王五", 40, 8888.88), new Employee("赵六", 21, 9999.99), new Employee("周七", 42, 11111.11));
@Test
public void test1() {
List<Employee> list = filterEmployeesByAge(employees);
for (Employee employee : list) {
System.out.println(employee);
}
System.out.println("-----------------------");
List<Employee> list2 = filterEmployeeBySalary(employees);
for (Employee employee : list2) {
System.out.println(employee);
}
}
/*
* 需求1:获取当前公司中员工年龄大于35的员工信息
*/
public List<Employee> filterEmployeesByAge(List<Employee> list) {
List<Employee> emps = new ArrayList<Employee>();
for (Employee emp : list) {
if (emp.getAge() > 35) {
emps.add(emp);
}
}
return emps;
}
/*
* 需求2:获取当前公司中员工工资大于7000的员工信息
*/
public List<Employee> filterEmployeeBySalary(List<Employee> list) {
List<Employee> emps = new ArrayList<Employee>();
for (Employee emp : list) {
if (emp.getSalary() > 7000) {
emps.add(emp);
}
}
return emps;
}
}
优化方式一:使用策略设计模式
定义策略接口:MyPredicate.java
public interface MyPredicate<T> {
boolean test(T t);
}
定义实现类:通过年龄过滤员工
FilterEmployeeByAge.java
public class FilterEmployeeByAge implements MyPredicate<Employee> {
@Override
public boolean test(Employee t) {
return t.getAge() > 35;
}
}
定义实现类:通过薪资过滤员工
FilterEmployeeBySalary.java
public class FilterEmployeeBySalray implements MyPredicate<Employee> {
@Override
public boolean test(Employee t) {
return t.getSalary() > 7000;
}
}
测试:
@Test
public void test2() {
List<Employee> list = filterEmployeeByPolicy(employees, new FilterEmployeeByAge());
for (Employee employee : list) {
System.out.println(employee);
}
System.out.println("-------------------------------");
List<Employee> list2 = filterEmployeeByPolicy(employees, new FilterEmployeeBySalray());
for (Employee employee : list2) {
System.out.println(employee);
}
}
public List<Employee> filterEmployeeByPolicy(List<Employee> list, MyPredicate<Employee> mp) {
List<Employee> emps = new ArrayList<>();
for (Employee employee : list) {
if (mp.test(employee)) {
emps.add(employee);
}
}
return emps;
}
优化方式二:匿名内部类
@Test
public void test3() {
List<Employee> list = filterEmployeeByPolicy(employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee t) {
return t.getSalary() > 7000;
}
});
for (Employee employee : list) {
System.out.println(employee);
}
System.out.println("--------------------");
List<Employee> list2 = filterEmployeeByPolicy(employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee t) {
return t.getAge() > 35;
}
});
for (Employee employee : list2) {
System.out.println(employee);
}
}
优化方式三:Lambda表达式
@Test
public void test4() {
List<Employee> list = filterEmployeeByPolicy(employees, (e) -> e.getSalary() > 7000);
list.forEach(System.out::println);
System.out.println("--------------------");
List<Employee> list2 = filterEmployeeByPolicy(employees, (e) -> e.getAge() > 35);
list2.forEach(System.out::println);
}
优化方式四:Stream API
@Test
public void test7() {
employees.stream().filter((e) -> e.getSalary() > 7000).forEach(System.out::println);
System.out.println("-----------------------");
employees.stream().filter((e) -> e.getAge() > 35).forEach(System.out::println);
System.out.println("-----------------------");
employees.stream().map(Employee::getName).forEach(System.out::println);
}
3、Lambda表达式基础语法
Java8中引入了一个新的操作符“ -> ”,该操作符称为箭头操作符或者Lambda操作符。箭头操作符将Lambda表达式拆分成两个部分:
左侧:Lambda表达式的参数列表。
右侧:Lambda表达式中所有需要执行的功能,即Lambda体。
语法格式一:无参数,无返回值
() -> System.out.println("HelloLambda!");
@Test
public void test1() {
int num = 0; // jdk1.8之前,必须加final修饰。jdk1.8可以不写,默认会加上final
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!" + num);
}
};
r.run();
System.out.println("-----------------------");
Runnable r2 = () -> System.out.println("Hello Lambda!");
r2.run();
}
语法格式二:有一个参数,并且无返回值
(x)-> System.out.println(x);
@Test
public void test2() {
Consumer<String> con = (x) -> System.out.println(x);
con.accept("哈哈哈");
}
语法格式三:若只有一个参数,小括号可以省略不写
x-> System.out.println(x);
语法格式四:有两个以上的参数,有返回值,并且Lambda体中有多条语句
@Test
public void test3() {
Comparator<Integer> com = (x, y) -> {
System.out.println("函数式接口");
return Integer.compare(x, y);
};
}
语法格式五:若Lambda体中只有一条语句,return和大括号可以省略不写
Comparator<Integer>com = (x, y) -> Integer.compare(x, y);语法格式六:Lambda表达式的参数列表的参数类型可以省略不写,因为编译器可以通过上下文推断出数据类型,即“类型推断”
@Test
public void test4() {
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
// 类型推断:参数列表的参数类型可写可不写
Comparator<Integer> com2 = (Integer x, Integer y) -> Integer.compare(x, y);
}
Lambda表达式需要“函数式接口”的支持。
函数式接口:接口中只有一个抽象方法时,称为函数式接口。可以使用@FunctionalInterface注解修饰,可以用来检查被修饰的接口是否是函数式接口。
/**
* 函数式接口,使用@FunctionalInterface修饰,只能有一个抽象方法
*/
@FunctionalInterface
public interface MyFun {
Integer getValue(Integer num);
}
4、Java8内置的四大核心函数式接口
Consumer<T>:消费型接口
void accept(T t);
Supplier<T>:供给型接口
T get();
Function<T,R>:函数型接口
R apply(T t);
Predicate<T>:断言型接口
boolean test(T t);
内置函数式接口演示:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.junit.Test;
public class TestLambda3 {
// Predicate<T>:断言型接口
@Test
public void test4() {
List<String> list = Arrays.asList("abc", "javascript", "html", "sql", "spring");
List<String> result = filterStr(list, (str) -> str.length() > 3);
for (String ret : result) {
System.out.println(ret);
}
}
// 需求:将满足条件的字符串放入集合中去
public List<String> filterStr(List<String> list, Predicate<String> pre) {
List<String> strList = new ArrayList<>();
for (String str : list) {
if (pre.test(str)) {
strList.add(str);
}
}
return strList;
}
// Function<T,R>:函数型接口
@Test
public void test3() {
String ret = strHandler(" hello java ", (str) -> str.trim());
System.out.println(ret);
String subStr = strHandler("namespace", (str) -> str.substring(0, 4));
System.out.println(subStr);
}
// 需求:用于处理字符串
public String strHandler(String str, Function<String, String> fun) {
return fun.apply(str);
}
// Supplier<T>:供给型接口
@Test
public void test2() {
List<Integer> list = getNumList(10, () -> (int) (Math.random() * 100));
for (Integer num : list) {
System.out.println(num);
}
}
// 需求:产生指定个数的整数,并放入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = sup.get();
list.add(n);
}
return list;
}
// Consumer<T>:消费型接口:
@Test
public void test1() {
happy(10000, (m) -> System.out.println("春节打年货,平均每年消费:" + m + "元"));
}
public void happy(double money, Consumer<Double> con) {
con.accept(money);
}
}
5、方法引用与构造器引用
方法引用:若Lambda体中的内容有方法已经实现了,我们可以使用方法引用(可以理解为方法引用是Lambda表达式的另外一种表现形式)。
主要有三种语法格式:
对象::实例方法名
类::静态方法名
类::实例方法名
注意:
1、Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。
2、若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method代码示例:
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.junit.Test;
public class TestMethodRef {
// 类::实例方法名
@Test
public void test4() {
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
BiPredicate<String, String> bp2 = String::equals;
}
// 类::静态方法名
@Test
public void test3() {
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
Comparator<Integer> com2 = Integer::compareTo;
}
// 对象::实例方法名
@Test
public void test1() {
PrintStream ps1 = System.out;
Consumer<String> con = (x) -> ps1.println(x);
PrintStream ps = System.out;
Consumer<String> con1 = ps::println;
Consumer<String> con2 = System.out::println;
con2.accept("hello lambda");
}
@Test
public void test2() {
Employee emp = new Employee();
Supplier<String> sup = () -> emp.getName();
String name = sup.get();
System.out.println(name);
Supplier<String> sup2 = emp::getName;
String name2 = sup2.get();
System.out.println(name2);
}
}
构造器引用:
格式:ClassName::new
注意:需要调用的构造器的参数列表要与函数式接口中的抽象方法的参数列表保持一致。
// 构造器引用
@Test
public void test5() {
Supplier<Employee> sup = () -> new Employee();
// 构造器引用方式
Supplier<Employee> sp2 = Employee::new; // 使用的无参构造器
Employee emp = sp2.get();
System.out.println(emp);
}
@Test
public void test6() {
Function<Integer, Employee> fun = (x) -> new Employee(x);
Employee emp = fun.apply(1);
System.out.println(emp);
// 构造器引用方式
Function<Integer, Employee> fun2 = Employee::new;
Employee emp2 = fun2.apply(100);
System.out.println(emp2);
BiFunction<Integer, Integer, Employee> bf = Employee::new;
Employee emp3 = bf.apply(202, 28);
System.out.println(emp3);
}
数组引用:
格式:Type::new
代码示例:@Test
public void test7() {
Function<Integer, String[]> fun = (x) -> new String[x];
String[] arr = fun.apply(10);
System.out.println(arr.length);
Function<Integer, String[]> fun2 = String[]::new;
String[] arr2 = fun2.apply(20);
System.out.println(arr2.length);
}