目录
jdk1.8后java提供了函数式编程,即一种抽象的编程范式,允许把函数作为参数传入另一个函数,也允许返回一个函数,我们常常使用的lambda就是一种函数式编程。
在jdk1.8中内置了四个常用的函数式编程的接口,分别是:Function、Predicate、Supplier、Consumer。下面我们来分别介绍下这些类。
1、Function
function:接收一个参数,并指定返回参数类型。Function<T, R>:T:表示提交参数,R:表示返回参数。
1.1 代码示例
我们通过代码来实现function,看看它提供的一些方法:
/**
* @author: jiangjs
* @description: 使用Predicate和Function
* @date: 2023/2/17 16:06
**/
public class usePredicateAndFunction {
public static void main(String[] args) {
int len = strApply(String::length,"word");
System.out.println("字符串长度:"+len);
Integer compose = moreCompose(5, i -> i + 1, i -> i * 5);
System.out.println("compose:先计算输入逻辑,再计算当前逻辑:"+compose);
Integer andThen = moreAndThen(5, i -> i + 1, i -> i * 5);
System.out.println("andThen:先计算当前逻辑,再计算传入逻辑:"+andThen);
}
/**
* 字段长度
*/
public static Integer strApply(Function<String,Integer> func,String word){
return func.apply(word);
}
/**
* 多表达式计算,使用compose
*/
@SafeVarargs
public static Integer moreCompose(Integer initVal, Function<Integer, Integer>... func){
Function<Integer, Integer> fir = null;
AtomicInteger ai = new AtomicInteger(0);
for (Function<Integer, Integer> function : func) {
int val = ai.intValue();
fir = val ==0 ? function : fir.compose(function);
ai.incrementAndGet();
}
assert fir != null;
return fir.apply(initVal);
}
/**
* 多表达式计算,使用andThen
*/
@SafeVarargs
public static Integer moreAndThen(Integer initVal, Function<Integer, Integer>... func){
Function<Integer, Integer> fir = null;
AtomicInteger ai = new AtomicInteger(0);
for (Function<Integer, Integer> function : func) {
int val = ai.intValue();
fir = val ==0 ? function : fir.andThen(function);
ai.incrementAndGet();
}
assert fir != null;
return fir.apply(initVal);
}
}
执行结果:
在上述代码中,第一个是计算字符串长度,定义的Function是Function<String,Integer>,也就是传入的是String类型,返回的是Integer。第二、三个则定义多个function的计算,分别使用的compose、andThen这两个方法是接口定义的默认方法,在查看源码时,我们来讲讲这两个方法。
1.2 源码
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
从源码中,我们可以看到该接口提供了四个方法:apply,compose,andThen,identity。
apply
:执行T参数,返回R。
compose
:先计算输入逻辑,再计算当前逻辑,在上面的代码实现中,Integer compose = moreCompose(5, i -> i + 1, i -> i * 5)
,i -> i + 1表达式就是当前逻辑,而i -> i * 5表示输入的逻辑。因此先计算i -> i * 5,在计算i -> i + 1,所以结果是:26。
andThen
:先计算当前逻辑,再计算传入逻辑,同样的Integer compose = moreAndThen(5, i -> i + 1, i -> i * 5)
,此时则先计算当前逻辑:i -> i + 1,再计算传入逻辑:i -> i * 5,其结果:30。
identity
:按照源码解析是一个总是返回其输入函数的函数。也就是说输入的是什么,返回就是什么。我们在平时开发中常常用到Collectors.toMap,其设置value就可以使用。
2、Predicate
Predicate:为我们提供了最基本的一些逻辑判断,例如:与,或,非等。在jdk1.8提供的很多处理集合的方法中就使用了这个接口,例如:filter
2.1 代码示例
我们通过代码来实现Predicate,看看它提供的一些方法:
/**
* @author: jiangjs
* @description: 使用Predicate和Function
* @date: 2023/2/17 16:06
**/
public class usePredicateAndFunction {
public static void main(String[] args) {
boolean conform = conformLength(s -> s.length() > 5, "helloWord");
System.out.println("字符串长度是否符合:"+conform);
boolean nonConform = nonConform(s -> s.length() > 5, "helloWord");
System.out.println("字符串长度是否符合(非):"+nonConform);
boolean contain = moreContain("helloWord", s -> s.contains("h"),
s -> s.contains("W"),
s -> s.contains("Word"));
System.out.println("字符串是否包含多条件:"+contain);
boolean single = singleContain("helloWord", s -> s.contains("hello"),
s -> s.contains("b"),
s -> s.contains("a"));
System.out.println("字符串是否包含单条件:"+single);
}
/**
* 是否匹配长度
*/
public static boolean conformLength(Predicate<String> predicate,String word){
return predicate.test(word);
}
/**
* 非
*/
public static boolean nonConform(Predicate<String> predicate,String word){
return predicate.negate().test(word);
}
/**
* 多条件匹配
*/
@SafeVarargs
public static boolean moreContain(String data, Predicate<String>... predicates){
Predicate<String> more = null;
AtomicInteger ai = new AtomicInteger(0);
for (Predicate<String> predicate : predicates) {
int val = ai.intValue();
more = val == 0 ? predicate : more.and(predicate);
ai.incrementAndGet();
}
assert more != null;
return more.test(data);
}
/**
* 多条件匹配
*/
@SafeVarargs
public static boolean singleContain(String data, Predicate<String>... predicates){
Predicate<String> single = null;
AtomicInteger ai = new AtomicInteger(0);
for (Predicate<String> predicate : predicates) {
int val = ai.intValue();
single = val == 0 ? predicate : single.or(predicate);
ai.incrementAndGet();
}
assert single != null;
return single.test(data);
}
}
上述代码中我们实现了:test,negate,and,or方法,这也是Predicate提供的方法。
执行结果:
2.2 源码
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
test
:执行参数,验证数据是否符合规则,返回boolean
and
:与,就相当于我们代码中的&&,也存在短路特性,只有所有条件都满足才会返回true,在示例代码中,我们使用 moreContain这个方法,验证了helloWord是否包含了多条件h,W,Word,都满足条件,所以返回结果为true。
or
:或,相当于我们代码中的||,多个条件只要有一个成立就返回true,在示例代码中,我们使用singleContain这个方法,验证了helloWord是否包含了条件hello,b,a,满足了包含hello条件,返回true。
negate
:非,也就是我们代码里面的!,示例代码中判断helloWord长度是否大于5,条件是成立的,使用negate则返回了false。
isEqual
:验证数据是否相等。
3、Supplier
Supplier:供给型接口,不需要传入参数,直接执行参数,返回执行结果
3.1 代码示例
/**
* @author: jiangjs
* @description: 使用Supplier、Consumer
* @date: 2023/4/26 9:53
**/
public class useSupplierAndConsumer {
public static void main(String[] args) {
List<String> data = Arrays.asList("张无忌","张翠山","张三丰","小昭","赵敏","白眉鹰王");
//使用Supplier获取姓张的人
Supplier<List<String>> supplier = () -> {
List<String> filter = new ArrayList<>();
for (String datum : data) {
if (datum.startsWith("张")){
filter.add(datum);
}
}
return filter;
};
List<String> result = supplier.get();
System.out.println("获取Supplier结果:" + result);
}
}
上述示例中,我们获取了姓张的名字,在lambda中并未传递参数,而是直接使用定义的集合,进行过滤的,然后返回收集到的数据。
执行结果:
3.2 源码
@FunctionalInterface
public interface Supplier<T> {
T get();
}
get
:即获取操作结果
4、Consumer
Consumer:属于消费型接口,需要传入参数,执行lambda表达式,没有返回值,我们常使用的forEach中就使用了Consumer。
4.1 代码示例
/**
* @author: jiangjs
* @description: 使用Supplier、Consumer
* @date: 2023/4/26 9:53
**/
public class useSupplierAndConsumer {
public static void main(String[] args) {
//使用Consumer打印姓张的人,由于Consumer没有返回值,只能打印结果
Consumer<String> consumer = (name) -> {
if (name.startsWith("张")){
System.out.println("张姓人员:" + name);
}
};
Consumer<String> secConsumer = (name) -> {
if (Objects.equals("张翠山",name)){
System.out.println("找到了:" + name);
}
};
Objects.requireNonNull(consumer);
for (String datum : data) {
moreAndThen(datum,consumer,secConsumer);
}
}
@SafeVarargs
private static void moreAndThen(String data, Consumer<String>... consumers){
Consumer<String> exeConsumer = null;
for (Consumer<String> consumer : consumers) {
if (Objects.isNull(exeConsumer)){
exeConsumer = consumer;
continue;
}
exeConsumer = exeConsumer.andThen(consumer);
}
assert exeConsumer != null;
exeConsumer.accept(data);
}
}
由于Consumer没有返回值,我们只能打印出姓张的名字。
执行结果:
其实从上述打印结果的代码可以发现,与forEach的源码类似的。
在jdk中还提供了IntConsumer
、DoubleConsumer
、LongConsumer
、BiConsumer
这些Consumer,使用方法和上面一样。
4.2 源码
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
accept
:执行参数,如上述代码示例中的打印张姓人员。
andThen
:与function中的andThen一样,先计算当前逻辑,再计算传入逻辑。示例代码中,先查询张姓名字,再查找某个特殊的名字。
5、自定义函数式编程
查看上面4个函数式编程接口会发现每个接口上都是用了注解:@FunctionalInterface
也就是jdk提供声明该接口是函数式编程接口。
我们来实现一个自定义函数式编程接口,不需要返回值也不需要传入任何参数。
5.1 声明接口
/**
* @author: jiangjs
* @description: 声明函数式编程接口
* @date: 2023/4/14 10:03
**/
@FunctionalInterface
public interface ExecuteBusiness {
/**
* 执行业务逻辑
*/
void execute();
}
5.2 使用接口
/**
* @author: jiangjs
* @description:
* @date: 2023/4/26 9:53
**/
public class useExecuteBusiness{
public static void main(String[] args) {
List<String> data = Arrays.asList("张无忌","张翠山","张三丰","小昭","赵敏","白眉鹰王");
//使用自定义函数式编程接口
ExecuteBusiness eb = () -> {
StringJoiner sj = new StringJoiner(",");
for (String datum : data) {
sj.add(datum);
}
System.out.println("武侠人物:" + sj.toString());
};
eb.execute();
}
执行结果:
以上就是我对jdk1.8后提供的函数式编程的理解,以及自定义实现函数式编程,如有理解有误的希望大家指正。