java -Function、Predicate、Supplier、Consumer及自定义实现

目录

1、Function

1.1 代码示例

1.2 源码

2、Predicate

2.1 代码示例

2.2 源码

3、Supplier

3.1 代码示例

3.2 源码

4、Consumer

4.1 代码示例

4.2 源码

5、自定义函数式编程

5.1 声明接口

5.2 使用接口


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中还提供了IntConsumerDoubleConsumerLongConsumerBiConsumer这些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后提供的函数式编程的理解,以及自定义实现函数式编程,如有理解有误的希望大家指正。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值