一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情。
lambda
什么是 lambda 表达式
- lambda 表达式本质就是作为只有一个方法的接口的实例。也被称函数式接口。
- 我们在给一个函数式接口创建实例的时候会使用 lambda 表达式
lambda 格式
lambda格式:(形参 参数名)-> {方法体}
接下来我们看一下格式的对比
-
传统格式
Consumer<String> con = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; con.accept("Consumer"); -
lambda 格式
Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("lambda");(String s): lambda 形参列表。->:lambda 关键字{System.out.println(s);};:lambda 方法体。
我们可以看到使用 lambda 格式要比传统格式写的东西更少更简洁。
只需要方法的形参和方法体即可。
lambda 语法
语法一:无参返回值
@Test
public void test1() {
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("r1启动");
}
};
r1.run();
System.out.println("------------- lambda -----------------");
Runnable r2 = () -> System.out.println("r2启动");
r2.run();
}
如果没有参数的话,lambda 参数列表可以为空。
语法二:有参无返回值
@Test
public void test2() {
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("Consumer");
System.out.println("------------- lambda -----------------");
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("lambda");
}
如果方法体的类型是 void,lambda 方法体可以不用写 return 关键字。
语法三:数据类型省略 类型推断
@Test
public void test3() {
Consumer<String> con1 = (s) -> {
System.out.println(s);
};
con1.accept("类型推断");
}
如果接口类是泛型的话,可以不写 lambda 参数可以不写属性,只用写括号和参数名即可。
语法四: Lambda 只有一个参数,参数的小括号可以省略
@Test
public void test4() {
Consumer<String> con1 = s -> {
System.out.println(s);
};
con1.accept("类型推断");
}
如果接口类是泛型且参数只有一个,可以不写 lambda 参数属性和 ‘( )’,只用写参数名即可了
语法五:lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test5() {
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com1.compare(2, 1));
System.out.println("------------- lambda -----------------");
Comparator<Integer> com2 = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(3, 2));
}
如果有多个参数,只需要在 lambda 参数列表里写上对应的参数数量即可。
语法六:当 lambda 体只有一条执行语句, return 和大括号可以省略。
@Test
public void test6() {
Comparator<Integer> com2 = (o1, o2) -> {
return o1.compareTo(o2);
};
System.out.println(com2.compare(3, 2));
System.out.println("--------------------------------");
Comparator<Integer> com3 = (o1, o2) -> o1.compareTo(o2);
System.out.println(com3.compare(3, 2));
}
【总结】
- ->: lambda 关键字
- (): lambda 形参列表。其中的类型可以省略;如果只有一个参数可以将 () 也省略
- {}: lambda 方法体。如果只有一条执行语句,return 关键字和 {} 也可以省略。
自定义接口
我们可以创建接口类并且使用 lambda 来创建接口的实例
-
首先我们来创建接口
package com.hyz.lambda.lambdaTest; /** * @author workplace * @date 2022/4/23 15:48 */ @FunctionalInterface public interface LambdaInterface { void l1(String a, int b); } -
通过 lambda 来创建接口实例
@Test public void test7() { LambdaInterface lambdaInterface = (String a, int b) -> { System.out.println(a); System.out.println(b); }; lambdaInterface.l1("1", 2); }
【注意】
- 我们创建的接口必须是只有一个方法的接口。
- 在创建接口的时候尽量加上 @FunctionalInterface 注解。这样子就可以自动帮你检测你的接口是否正确。
Java 内置的函数式接口
接下来我们来写一下例子
Consumer<T> 消费型接口
@Test
public void test1() {
Consumer<String> consumer = s -> {
s = s + s;
System.out.println(s);
};
consumer.accept("这个字符串会被重复一遍;");
}
通过函数式接口可以修改 lambda 方法体对指定类型对象进行操作。
Supplier<T> 供给型接口
@Test
public void test2() {
Supplier<String> supplier = () -> "字符串对象被获取";
System.out.println(supplier.get());
}
可以获取自定义的类。
Function<T, R> 函数型接口
@Test
public void test3() {
Function<String, Integer> function = a -> a.length();
System.out.println(function.apply("这个字符串会被重复一遍;"));
}
Predicate<T> 判定型接口
@Test
public void test4() {
Predicate<String> predicate = a -> a.contains("关键字");
System.out.println(predicate.test("是否存有关键字"));
}
方法引用
什么是方法引用
- 方法引用本质上就是 lambda 表达式。所以方法引用是函数接口的实例。
- 当 lambda 方法体内的操作已有实现的方法,可以使用方法引用。
方法引用使用格式
方法引用是针对 lambda 表达式的改变。所以本质上使用原来的 lambda 表达式也可以的。
@Test
public void test1() {
Consumer<String> con1 = s -> System.out.println(s);
con1.accept("你好程序员");
System.out.println("----------------------方法引用--------------------------");
Consumer<String> con2 = System.out::println;
con2.accept("helloProgrammer");
}
【格式】:类( 或对象 ) :: 方法名
具体分为三种情况
- 情况一: 对象 :: 非静态方法
- 情况二: 类 :: 静态方法
- 情况三: 类 :: 非静态方法
情况一和情况二:要求【函数式接口的抽象方法的形参列表和返回值】与【方法引用的形参列表和返回值】相同。
情况三
可以理解为,当 lambda 表达式的方法体内的操作是能够通过某些类的方法实现的话,可以使用方法引用。
对象 :: 方法
class People{
private String name;
public People(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
@Test
public void test2() {
People people = new People("hyz");
Supplier<String> sup1 = () -> people.getName();
System.out.println(sup1.get());
System.out.println("----------------------方法引用--------------------------");
Supplier<String> sup2 = people::getName;
System.out.println(sup2.get());
}
【分析】
- 本来 lambda 方法区内存在一个 getName 的方法。
- 但是这个方法已经在 People 类中被实现了(已经被完全写好,只需要传参调用或直接调用)。
- People 方法的参数和返回值和函数式接口的参数和返回值一致,所以可以使用 方法引用。
- 我们可以通过 对象名 :: 方法名 来代替 lambda 表达式。
类 :: 静态方法
@Test
public void test3() {
Comparator<Integer> con1 = (t1, t2) -> Integer.compare(t1, t2);
System.out.println(con1.compare(2, 3));
System.out.println("----------------------方法引用--------------------------");
Comparator<Integer> con2 = Integer::compare;
System.out.println(con2.compare(2,3));
}
【分析】
- lambda 方法体内的方法已经被实现
- 被实现的方法的参数和返回值与函数式接口的参数和返回值相同
- 被实现的方法是静态方法,所以选择通过 【类名 :: 静态方法】 来引用。
类 :: 非静态方法
我们先创建一个 Book 类在来测试。
class Book{
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
@Test
public void test4() {
Book book = new Book("hyz");
Function<Book,String> fun1=new Function<Book, String>() {
@Override
public String apply(Book book) {
return book.getName();
}
};
System.out.println(fun1.apply(book));
System.out.println("----------------------lambda--------------------------");
Function<Book, String> fun2 = b -> b.getName();
System.out.println(fun2.apply(book));
System.out.println("----------------------方法引用--------------------------");
Function<Book, String> fun3 = Book::getName;
System.out.println(fun3.apply(book));
}
【分析】
- lambda 方法体已被 Book 类实现
- 被实现的方法的参数和返回值与函数式接口的参数和返回值不相同,但是两个参数之间的关系是相互调用
- 被实现的方法是非静态方法,所以选择通过 【类名 :: 非静态方法】 来引用。
方法引用的规律
- 先看 lambda 方法区的方法是否已经被实现
- 再看被实现的方法和对应的函数式接口的方法是否匹配
- 如果不会写就写 lambda 格式,实在不行就用匿名方法。