Java函数式编程Lambda表达式详解


什么是Lambda表达式

Lambda表达式在编程中通常表示一种简洁表示匿名函数的方法。即一个匿名的函数。


Jdk的原生函数式接口

有且只有一个抽象方法的接口被称为函数式接口,函数式接口适用于函数式编程的场景,Lambda就是Java中函数式编程的体现,可以使用Lambda表达式创建一个函数式接口的对象,一定要确保接口中有且只有一个抽象方法,这样Lambda才能顺利的进行推导。

@FunctionalInterface 注解可用于一个接口的定义上,一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法(equal和hashcode方法不算),否则将会报错。但是这个注解不是必须的,只要符合函数式接口的定义,那么这个接口就是函数式接口。

Consumer<T>消费接口

Consumer是一个消费函数式接口,主要针对的是消费(1…n 入参, 无返回) 这个场景,它的代码定义如下:

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

通过泛型 T 定义了一个入参,但是没有返回值,它代表你可以针对这个入参做一些自定义逻辑,比较典型的例子是 forEach 方法。

List<String> list = Lists.newArrayList("1", "2", "3", "4", "5", "6");
list.forEach(System.out::println); //打印数组

Supplier<T>供给接口

Supplier是一个供给类的函数式接口,它主要针对的是获取(无入参,有返回) 这个场景,它的代码定义如下:

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

通过泛型 T 定义了一个返回值类型,但是没有入参,它代表你可以针对调用方获取某个值,比较典型的例子是 Stream 中的 collect 方法,通过自定义传入我们想要取得的某种对象进行对象收集。

List<String> list = Lists.newArrayList("1", "2", "3", "4", "5", "6");
List<String> newList = list.stream().filter(x -> x >= 2).collect(Collectors.toList()); 
// 将大于等于2的数重新收集成一个集合,其中Collectors.toList()的函数原型为 
// new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,(left, right) -> { left.addAll(right); return left; },CH_ID)
// 原型中的ArrayList::new即为Supplier类型

Function<T,R>函数型接口

Function 接口的名字不太能轻易看出来它的场景,它主要针对的则是 转换(有入参,有返回,其中T是入参,R是返回) 这个场景,其实说转换可能也不太正确,它是一个覆盖范围比较广的场景,你也可以理解为扩展版的Consumer,接口定义如下:

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

通过一个入参 T 进行自定义逻辑处理,最终得到一个出参 R,比较典型的例子是 Stream 中的 map 系列方法和 reduce 系列方法。

List<String> list = Lists.newArrayList("1", "2", "3", "4", "5", "6");
List<Integet> newList = list.stream().map(Integer::parseInt).collect(Collectors.toList());
// map将list中所有的元素的类型由 String 通过 Integer.parseInt的方式转换为Intger。 简单来说就是A => B;

Predicate<T,R>断言接口

Predicate主要针对的是判断(有入参,有返回,凡是返回的类型固定为Boolean。可以说Function 是包含Predicate的 ) 这个场景,它的代码定义如下:

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

通过泛型 T 定义了一个入参,返回了一个布尔值,它代表你可以传入一段判断逻辑的函数,比较典型的例子是 Stream 中的 filter方法。

List<String> list = Lists.newArrayList("1", "2", "3", "4", "5", "6");
List<String> newList = list.stream().filter(x -> x >= 2).collect(Collectors.toList()); 
// 将大于等于2的数重新收集成一个集合,filter中的 x -> x >= 2就是Predicate接口

Stream流

Java Stream(流)是一种新的处理数据的方式,它是Java 8引入的一个重要特性,旨在提高处理集合数据的效率和可读性,可以很轻松的完成诸如:过滤、分组、收集、归约这类操作。Stream API提供了一种声明式的方式来处理数据,允许你以更简洁、更函数式的方式来操作序列化的数据集。

其中Stream的操作大致分为两类

  • 中间型操作
  • 终结型操作

其中转换型操作又分为有状态和无状态两类。有状态是本次的结果需要依赖于前面的处理结果,而无状态则是不依赖。简单来讲就是无状态方法可以互相调换位置,而有状态方法不能调换位置。

中间型操作

中间型操作就是返回值依旧是stream类型的方法。api如下:

API功能说明无状态操作
filter()按照条件过滤符合要求的元素, 返回新的stream流
map()将已有元素转换为另一个对象类型,一对一逻辑,返回新的stream流
peek()对stream流中的每个元素进行逐个遍历处理,返回处理后的stream流
flatMap()将已有元素转换为另一个对象类型,一对多逻辑,即原来一个元素对象可能会转换为1个或者多个新类型的元素,返回新的stream流
limit()仅保留集合前面指定个数的元素,返回新的stream流
skip()跳过集合前面指定个数的元素,返回新的stream流
concat()将两个流的数据合并起来为1个新的流,返回新的stream流
distinct()对Stream中所有元素进行去重,返回新的stream流
sorted()对stream中所有的元素按照指定规则进行排序,返回新的stream流

除了上述api外,还有部分例如mapToInt、mapToLong、mapToDouble返回值为XStream(X为Double,Long,Int)的方法,这些方法均可用map()方法代替,使用方法除终结型操作中的collect有区别外,其他api使用无区别。

终结型操作

终结型操作与中间型相反,返回值是非Stream类型的。api如下:

API功能说明
count()返回stream处理后最终的元素个数
max()返回stream处理后的元素最大值
min()返回stream处理后的元素最小值
findFirst()找到第一个符合条件的元素时则终止流处理
findAny()找到任何一个符合条件的元素时则退出流处理,这个对于串行流时与findFirst相同,对于并行流时比较高效,任何分片中找到都会终止后续计算逻辑
anyMatch()返回一个boolean值,类似于isContains(),用于判断是否有符合条件的元素
noneMatch()返回一个boolean值, 用于判断是否所有元素都不符合条件
allMatch()返回一个boolean值,用于判断是否所有元素都符合条件
collect()将流转换为指定的类型,通过Collectors进行指定
reduce()将一个Stream中的所有元素反复结合起来,得到一个结果
toArray()将流转换为数组
iterator()将流转换为Iterator对象
foreach()无返回值,对元素进行逐个遍历,然后执行给定的处理逻辑

自定义函数式接口

定义函数式接口

为了表明这是一个函数式接口,通常会在注释中添加@FunctionalInterface ,但这不是强制性的,只要保证接口只包含一个抽象方法即可。

例如,我们定义一个接受两个整数参数并返回一个整数结果的函数式接口:

@FunctionalInterface
public interface IntBinaryOperatorCustom {
    int applyAsInt(int left, int right);
}

这里的applyAsInt就是该接口的唯一抽象方法,它接收两个int类型的参数,并返回一个int类型的结果。

使用函数式接口

使用这个函数式接口。有两种主要的使用方式:通过lambda表达式或通过方法引用。

IntBinaryOperatorCustom adder = (left, right) -> left + right;
int result = adder.applyAsInt(5, 3); // 结果将是8
System.out.println(result);

-----------------------------------------------------------------

IntBinaryOperatorCustom adder = (left, right) -> left * right;
int result = adder.applyAsInt(5, 3); // 结果将是15
System.out.println(result);

这种方式使得代码更加灵活和模块化,特别是在处理回调函数或事件驱动的编程场景下非常有用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java码农杂谈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值