Java8_Lambda表达式_详解
文章目录
一、什么是Lambda表达式?
Lambda 表达式是一个匿名函数,没有名称,但是有参数列表,函数主体,返回类型,可能还有一个可以抛出的异常的列表
Lambda表达式由三部分构成,参数列表,箭头,lambda主体
1)基础语法
1)->
:箭头表达式
-
箭头左边:参数列表 ,无参使用()
-
箭头右边:实现接口中抽象方法的实现代码
2)只有一个参数的话,可以省略参数的括号实现
3)只有一条语句的话,可以省略大括号,也可省略return
关键字
示例:开启一个线程,输出 “hello”
// 使用Lambda
@Test
public void test1() {
new Thread(() -> System.out.println("hello")).start();
}
// 等价于下面的匿名内部类的写法
@Test
public void test() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
t1.start();
}
2)Lambda使用场景?
可以在函数式接口上使用Lambda表达式
3)Lambad表达式可以做什么?
传递代码、行为参数化。使代码更清晰,灵活
4)Lambda表达式的实质
对接口的唯一抽象方法的实现
二、函数式接口
0)什么是函数式接口?
- 接口里面只有一个抽象方法,这个接口称为函数式接口。
- 使用
@FunctionalInterface
注解标识某个接口为函数式接口,如果自己写了一个不是函数式接口,而是用这个注解,会报错
1)消费者接口(Consumer)
Consumer:无返回值,对传入的参数操作即可
源码:
@FunctionalInterface
public interface Consumer<T> {
// 接收一个泛型T
void accept(T t);
//...
}
示例:接收一个字符串集合并打印
public <T> void forEach(List<T> list, Consumer<T> consumer){
for (T t : list) {
consumer.accept(t);
}
}
@Test
public void test3(){
List<String> list = new ArrayList<>(Arrays.asList("a","b","c"));
// 行为参数化
forEach(list,x->System.out.print(x+" "));
}
输出结果:
a b c
2)供应者接口(Supplier)
Supplier:返回一个T类型
源码:
@FunctionalInterface
public interface Supplier<T> {
// 返回给定的泛型参数类型
T get();
}
示例:创建num个整数放入list中
/**
* 创建num个整数放入list中
*/
public <T> void createNumbers(List<T> list, Supplier<T> supplier,int num){
for (int i = 0; i < num; i++) {
list.add(supplier.get());
}
}
@Test
public void test4(){
List<Integer> list = new ArrayList<>();
createNumbers(list, ()->new Random().nextInt(100),10);
System.out.println(list);
}
输出结果:
[16, 25, 29, 10, 11, 2, 15, 68, 41, 35]
3)判断式接口(Predicate)
Predicate:对传入的泛型参数做boolean判断
源码:
@FunctionalInterface
public interface Predicate<T> {
//判断
boolean test(T t);
//...
}
示例:对输出的字符判断是否空
@Test
public void test6(){
String str = "hell";
boolean bool = isEmpty(str, s -> s.isEmpty());
System.out.println(bool);
}
boolean isEmpty(String str, Predicate<String> pre){
return pre.test(str);
}
4)Function接口
Function<T,R>:接受一个泛型T的对象,返回一个泛型R的对象
源码:
@FunctionalInterface
public interface Function<T, R> {
/**
* 根据t,返回一个R类型
*/
R apply(T t);
示例:通过一个名字创建一个用户
class User {
String name;
public User(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
class Test{
@Test
public void test7(){
// 传递代码,行为参数化
User tom = createUser("Tom", name -> new User(name));
System.out.println(tom);
}
public User createUser(String name, Function<String,User> fun){
return fun.apply(name);
}
}
输出:
User{name='Tom'}
三、方法引用
类型 | ---------------------------------------------------------------------Lambda表达式 | -----------------------------------方法引用 | ---------------------------- |
---|---|---|---|
静态方法引用 | (args) -> 类名.staticMethod(args) | 类名 :: staticMethod | |
参数对象方法引用 | (arg0,rest)->arg0.instMethod(rest) | 类名 :: instMethod | arg0是lambda表达式的参数 |
外部对象方法引用 | (args) -> expr.instMethod(args) | expr:: instMethod | expr不是lambda表达式的参数 |
构建方法引用 | (args) -> new 类名(args) | 类名 :: new | 视情况而定 |
1)静态方法引用
抽象方法的参数列表要和调用的静态方法的参数一致
格式:类名::静态方法
@Test
public void test11() {
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
//简写
Comparator<Integer> com2 = Integer::compare;
}
2)外部对象方法引用
抽象方法的参数要和实例方法参数一致
外部对象:System.out
@Test
public void test8(){
Consumer<String> consumer = x-> System.out.println(x);
//简写
consumer = System.out::println;
consumer.accept("Hello");
}
3)参数对象方法引用
特点:我们在Lambda中引用一个对象的方法,而这个对象本身是Lambda中的一个参数
参数对象:x
@Test
public void test9() {
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
BiPredicate<String, String> bp2 = String::equals;
//Lambda参数列表中的第一个参数x,是实例方法的第一个调用者x。
//第二个参数y是实例方法的参数y。则可以使用
}
示例2
public Integer getLength(String str,Function<String,Integer> function){
return function.apply(str);
}
@Test
public void test5(){
// 普通lambda
Integer a1 = getLength("hello", s -> s.length());
// 方法引用
Integer a2 = getLength("demo", String::length);
System.out.println(a1+"..."+a2);
}
4)构造方法引用
构造函数 | 适用的函数式接口 | 写法 |
---|---|---|
Apple() | Supplier | Apple::new |
Apple(Integer size) | Function<Integer,Apple> | Apple::new |
Apple(String color,Integer size) | BiFunction<String,Integer,Apple> | Apple::new |
构造器的参数列表和抽象方法列表一致
Apple类
class Apple{
private Integer size;
private String color;
public Apple(Integer size, String color) {
this.size = size;
this.color = color;
}
public Apple(Integer size) {
this.size = size;
}
public Apple() {
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
@Override
public String toString() {
return "Apple{" +
"size=" + size +
", color='" + color + '\'' +
'}';
}
}
测试类
@Test
public void test6(){
Apple a1 = createApple_1(Apple::new);
Apple a2 = createApple_2(Apple::new, 12);
Apple a3 = createApple_3(Apple::new, 10, "red");
System.out.println(a1);
System.out.println(a2);
System.out.println(a3);
}
// 无参构造
public Apple createApple_1(Supplier<Apple> supplier){
return supplier.get();
}
// 一个参数构造
public Apple createApple_2(Function<Integer,Apple> function,Integer size){
return function.apply(size);
}
// 2个参数构造
public Apple createApple_3(BiFunction<Integer,String,Apple> function, Integer size,String color){
return function.apply(size,color);
}
输出结果
Apple{size=null, color='null'}
Apple{size=12, color='null'}
Apple{size=10, color='red'}
四、复合Lambda表达式
1)比较器复合
包括2个,一个逆序,一个比较器链
示例1:普通比较
Comparator<Apple> c1 = Comparator.comparing(Apple::getSize);
示例2:逆序
先按size排序,然后逆序(递减)
Comparator<Apple> c2 = Comparator.comparing(Apple::getSize).reversed();
示例3:比较器链
先比较size,如果size相同,比较color
Comparator<Apple> c3 = Comparator.comparing(Apple::getSize).thenComparing(Apple::getColor);
2)谓词复合
谓词复合包括3个,negate(取非),and(与),or(或)。可以重用现有的Predicate来创建更复杂的谓词
@Test
public void test7(){
// 红苹果
Predicate<Apple> redApple = apple -> apple.getColor().equals("red");
// negate()返回Predicate的非
Predicate<Apple> notRed = redApple.negate();
// 红苹果,并且size>10
Predicate<Apple> redAndSize = redApple.and(a -> a.getSize() > 10);
// 红苹果或绿苹果
Predicate<Apple> greenApple = redApple.or(a -> a.getColor().equals("green"));
}
3)函数复合
使用Function接口里2个默认方法(andThen和compose),把Function接口代表的Lambda表达式复合起来。
示例:andThen
将一个数+1,然后乘以2
@Test
public void test8(){
// 先将x+1,然后(x+1)*2
Function<Integer,Integer> step1 = x->x+1;
Function<Integer,Integer> step2 = x->x*2;
// 复合函数
Function<Integer, Integer> stepAll = step1.andThen(step2);
Integer res = stepAll.apply(10);
System.out.println(res); // 22
}
示例:compose
@Test
public void test9(){
// 先将x*2,然后(x*2)+1
Function<Integer,Integer> step1 = x->x+1;
Function<Integer,Integer> step2 = x->x*2;
// 复合函数
Function<Integer, Integer> stepAll = step1.compose(step2);
Integer res = stepAll.apply(10);
System.out.println(res); // 21
}
andThen和compose区别:
- step1.andThen(step2):先执行step1【10+1=11】,然后把step1的结果传入step2执行【11*2】
- step1.compose(step2):先step2【10*2=20】,然后step1【20+1】
五、小结
-
Lambda 表达式 可理解为一个匿名函数,没有名称,但是有参数列表,函数主体,返回类型,可能还有一个可以抛出的异常的列表
-
Lambda 表达式可以让我们简洁的传递代码
-
函数式接口:就是里面只有一个抽象方法的接口
-
只有在接受函数式接口的地方才可以使用Lambda表达式
-
方法引用让我们可以重复使用现有的方法实现并传递它们
-
Java8 自带了一些常用的函数式接口,放在java.util.function包里,包括(消费者,生产者,断言式,还有Function<T,R>等等