Lambda
匿名函数
Lambda是一个匿名函数,可以理解为一段可以传递的代码(将代码像数据一样传递);可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,是Java语言表达能力得到提升。
匿名内部类
@Test
public void test01(){
//匿名内部类
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
@Override
public boolean equals(Object obj) {
return false;
}
};
//调用
TreeSet<Integer> set = new TreeSet<>(comparator);
}
Lambda
@Test
public void test02(){
// Lambda 表达式
Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
TreeSet<Integer> set = new TreeSet<>(comparator);
}
演变过程:
- 垃圾代码 --> 策略模式 --> 匿名内部类 --> Lambda表达式
基础语法:
- 操作符:->
- 左侧:参数列表
- 右侧:执行代码块 / Lambda 体
口诀:
写死小括号,拷贝右箭头,落地大括号
左右遇一括号省
左侧推断类型省
语法格式:
无参数,无返回值:() -> sout
例如 Runnable接口:
public class Test02 {
int num = 10; //jdk 1.7以前 必须final修饰
@Test
public void test01(){
//匿名内部类
new Runnable() {
@Override
public void run() {
//在局部类中引用同级局部变量
//只读
System.out.println("Hello World" + num);
}
};
}
@Test
public void test02(){
//语法糖
Runnable runnable = () -> {
System.out.println("Hello Lambda");
};
}
}
有一个参数,无返回值
@Test
public void test03(){
Consumer<String> consumer = (a) -> System.out.println(a);
consumer.accept("我觉得还行!");
}
有一个参数,无返回值 (小括号可以省略不写)
@Test
public void test03(){
Consumer<String> consumer = a -> System.out.println(a);
consumer.accept("我觉得还行!");
}
有两个及以上的参数,有返回值,并且 Lambda 体中有多条语句
@Test
public void test04(){
Comparator<Integer> comparator = (a, b) -> {
System.out.println("比较接口");
return Integer.compare(a, b);
};
}
有两个及以上的参数,有返回值,并且 Lambda 体中只有1条语句 (大括号 与 return 都可以省略不写)
@Test
public void test04(){
Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
}
Lambda 表达式 参数的数据类型可以省略不写 Jvm可以自动进行 “类型推断”
函数式接口:
接口中只有一个抽象方法的接口 @FunctionalIterface
测试:
定义一个函数式接口:
@FunctionalInterface
public interface MyFun {
Integer count(Integer a, Integer b);
}
用一下:
@Test
public void test05(){
MyFun myFun1 = (a, b) -> a + b;
MyFun myFun2 = (a, b) -> a - b;
MyFun myFun3 = (a, b) -> a * b;
MyFun myFun4 = (a, b) -> a / b;
}
再用一下:
public Integer operation(Integer a, Integer b, MyFun myFun){
return myFun.count(a, b);
}
@Test
public void test06(){
Integer result = operation(1, 2, (x, y) -> x + y);
System.out.println(result);
}
案例
**案例一:**调用 Collections.sort() 方法,通过定制排序 比较两个 Employee (先按照年龄比,年龄相同按照姓名比),使用 Lambda 表达式作为参数传递
定义实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer id;
private String name;
private Integer age;
private Double salary;
}
定义 List 传入数据
List<Employee> emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
);
@Test
public void test01(){
Collections.sort(emps, (e1, e2) -> {
if (e1.getAge() == e2.getAge()){
return e1.getName().compareTo(e2.getName());
} else {
return Integer.compare(e1.getAge(), e2.getAge());
}
});
for (Employee emp : emps) {
System.out.println(emp);
}
}
**案例二:**声明函数式接口,接口中声明抽象方法,String getValue(String str); 声明类 TestLambda,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值;再将一个字符串的第二个和第四个索引位置进行截取字串
**案例三:**声明一个带两个泛型的函数式接口,泛型类型为<T, R> T 为参数,R 为返回值;接口中声明对应的抽象方法;在 TestLambda 类中声明方法,使用接口作为参数,计算两个 Long 类型参数的和;在计算两个 Long 类型参数的乘积
函数式接口
Java内置四大核心函数式接口:
函数式接口 参数类型 返回类型 用途
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)
消费型接口
@Test
public void test01(){
//Consumer
Consumer<Integer> consumer = (x) -> System.out.println("消费型接口" + x);
//test
consumer.accept(100);
}
函数型接口
@Test
public void test03(){
//Function<T, R>
String oldStr = "abc123456xyz";
Function<String, String> function = (s) -> s.substring(1, s.length()-1);
//test
System.out.println(function.apply(oldStr));
}
断言型接口
@Test
public void test04(){
//Predicate<T>
Integer age = 35;
Predicate<Integer> predicate = (i) -> i >= 35;
if (predicate.test(age)){
System.out.println("你该退休了");
} else {
System.out.println("我觉得还OK啦");
}
}
其他接口
方法引用
**定义:**若 Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”
语法格式:
对象 :: 实例方法
类 :: 静态方法
类 :: 实例方法
对象::实例方法
@Test
public void test01(){
PrintStream ps = System.out;
Consumer<String> con1 = (s) -> ps.println(s);
con1.accept("aaa");
Consumer<String> con2 = ps::println;
con2.accept("bbb");
}
**注意:**Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致
类::静态方法
@Test
public void test02(){
Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
System.out.println(com1.compare(1, 2));
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(2, 1));
}
类::实例方法
@Test
public void test03(){
BiPredicate<String, String> bp1 = (x, y) -> x.equals(y);
System.out.println(bp1.test("a","b"));
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("c","c"));
}
**条件:**Lambda 参数列表中的第一个参数是方法的调用者,第二个参数是方法的参数时,才能使用 ClassName :: Method
构造器引用
格式:
ClassName :: new
@Test
public void test04(){
Supplier<List> sup1 = () -> new ArrayList();
Supplier<List> sup2 = ArrayList::new;
}
**注意:**需要调用的构造器的参数列表要与函数时接口中抽象方法的参数列表保持一致
数组引用
语法:
Type :: new;