Java8函数式编程接口:Consumer、Supplier、Function、Predicate

简介: Java8函数式编程接口:Consumer、Supplier、Function、Predicate

开宗明义

java8的一个新特性就是在java.util.function中提供了四个函数式编程接口,分别是Consumer、Supplier、Function、Predicate

其中,

  • consumer即消费接口,传入一个参数,并对其进行相应的操作(有点类似于lambda表达式);
  • supplier即供给接口,可以传入数据,作为一个容器
  • function即方法接口主要是用作数据类型之间的转换
  • predicate即判断接口,传入参数,而后返回判断的结果true/false

接下来仔细瞅瞅这四种接口的源码以及怎么使用它们。

庖丁解牛

consumer

源码

Consumer接口的源码只有两个方法,一个方法用来接收入口参数,另外一个是为了实现流式操作

@FunctionalInterface
public interface Consumer<T> {

    /**
     *接收一个泛型入参
     */
    void accept(T t);

    /**
     * 是指在进行这个consumer之后,在将结果送进另外一个consumer,
     * 从而实现consumer的流式操作(一个接一个地进行)
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

注:@FunctionalInterface注解即是表明它是一个函数接口,另外的supplier、function、predicate同样如此。

示例

实现Consumer接口可以有三种方式,看下面:

public static void main(String[] args) {
        //1:平平无奇:直接创建接口并重载
        Consumer<String> consumer1 = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        Stream<String> stream1 = Stream.of("spring", "summer", "autumn", "winter");
        stream1.forEach(consumer1);
        System.out.println("********************");

        //2:偶有起色: 使用Java8另外一个特性,方法引用
        Consumer consumer2 = System.out::println;
        Stream<String> stream2 = Stream.of("spring", "summer", "autumn", "winter");
        stream2.forEach(consumer2);
        System.out.println("********************");

        //3:精彩绝伦,使用lambda表达式
        Consumer<String> consumer3 = (s) -> System.out.println(s);//lambda表达式返回的就是一个Consumer接口
        Stream<String> stream3 = Stream.of("spring", "summer", "autumn", "winter");
        stream3.forEach(consumer3);
    }
//改写自:https://cloud.tencent.com/developer/article/1488128

结果如下:

spring
summer
autumn
winter
********************
spring
summer
autumn
winter
********************
spring
summer
autumn
winter

注:Stream只能用一次,无法reuse,违规则报错,因此在上述测试的时候建立了多个Stream对象;


注:方法引用和lambda表达式就是consumer的实现,所以不用显示调用accept。


另外,对于集合类的foreach,是因为那么都实现了iterable接口,并且itrable接口支持Consumer:

public interface Iterable<T> {
    //forEach方法传入的就是Consumer
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
}

Supplier

源码

Supplier可以简单的视为就是一个存放指定类型的容器,需要时再取出来

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

在Optional类的orElseGet、orElse等API就使用了类Supplier,其中orElseGet的源码如下:

Optional.orElseGet(Supplier<? extends T>)

示例

和上面的Consumer一样,Supplier也可以通过三种方式来使用:

public void test_Supplier() {
        //1: 直接新建一个Supplier接口
        Supplier<Integer> supplier = new Supplier<Integer>() {
            @Override
            public Integer get() {
                //返回一个随机值
                return new Random().nextInt();
            }
        };
        System.out.println(supplier.get());
        System.out.println("********************");

        //2: 使用方法引用
        Supplier<Double> supplier2 = Math::random;
        System.out.println(supplier2.get());
        System.out.println("********************");

        //3: 使用lambda表达式
        supplier = () -> new Random().nextInt();
        System.out.println(supplier.get());
    }
//改写自:https://cloud.tencent.com/developer/article/1488128

结果如下:

-1704173567
********************
0.7995174190529915
********************
2009273688

Process finished with exit code 0

可知,Suppiler就是一个用来获取对应类型返回值的函数接口,返回值可以通过传入的参数来设定。

Function

源码

Function的抽象方法只有一个apply,即对传入的参数进行处理并返回,其他的都是辅助方法:

@FunctionalInterface
public interface Function<T, R> {   
    /**
    * 抽象方法: 对一个数据类型T加工得到另一个数据类型R
    */
    R apply(T t);

    /**
    * 组合函数,调用当前function之前调用
    */
    default <V> java.util.function.Function<V, R> compose(java.util
    .function.Function<? super V, ? extends T> before) {
    Objects.requireNonNull(before);
      return (V v) -> apply(before.apply(v));
     }

    /**
    * 组合函数,调用当前function之后调用
    */
    default <V> java.util.function.Function<T, V> andThen(java.util
    .function.Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
      return (T t) -> after.apply(apply(t));
    }

    /**
    *  静态方法,返回与原函数参数一致的结果。x=y
    */
    static <T> java.util.function.Function<T, T> identity() {
      return t -> t;
    }
  }

示例

Function接口的示例可以通过这个例子来一窥究竟:

public static void main(String[] args) {
        applyTest();
        andThenTest();
        composeTest();
        test();
    }

    //1、apply 示例
    private static void applyTest() {
        //示例1:利用lambda方式实现一个funciton,将String转换为Integer
        Function<String, Integer> function = x -> Integer.parseInt(x);
        Integer a = function.apply("100");
        System.out.println(a.getClass());
    }

    //2、andThen 示例——实现一个函数 y=10x + 10;
    private static void andThenTest() {
        //先执行 10 * x
        Function<Integer, Integer> function2 = x -> 10 * x;
        //通过andThen在执行 这里的x就等于上面的10 * x的值
        function2 = function2.andThen(x -> x + 10);
        System.out.println(function2.apply(2));
    }

    //3、compose 示例-实现一个函数 y=(10+x)2
    private static void composeTest() {
        Function<Integer, Integer> function3 = x -> x * 2;
        //先执行 x+10 在执行(x+10)*2顺序与上面相反
        function3 = function3.compose(x -> x + 10);
        System.out.println(function3.apply(3));
    }

    //4、综合示例
    //使用compose()、andThen()实现一个函数 y=(10+x)*2+10;
    private static void test() {
      //真正执行的第二步
      Function<Integer, Integer> function4 = x -> x * 2;
      //真正执行的第一步
      function4 = function4.compose(x -> x + 10);
      //真正执行的第三步
      function4 = function4.andThen(x -> x + 10);
      System.out.println(function4.apply(3));
    }

先自己想一想结果,再往下看!!

输出结果为:

class java.lang.Integer
30
26
36

Process finished with exit code 0

除了上面使用的 Function 接口,还可以使用下面这些 Function 接口。 IntFunction 、DoubleFunction 、LongFunction 、ToIntFunction 、ToDoubleFunction 、DoubleToIntFunction 等等,使用方法和上面一样。


Function 接口实现 apply 方法来做转换。

Predicate

源码

Predicate的源码如下,该方法的辅助方法较多:

public interface Predicate<T> {
    /**
     * 判断参数T是否满足要求,可以理解为 条件A
     */
    boolean test(T t);
    /**
     * 调用当前Predicate的test方法之后再去调用other的test方法,相当于进行两次判断
     * 可理解为 条件A && 条件B
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
    /**
     * 对当前判断进行"!"操作,即取非操作,可理解为 ! 条件A
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
    /**
     * 对当前判断进行"||"操作,即取或操作,可以理解为 条件A ||条件B
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * 对当前操作进行"="操作,即取等操作,可以理解为 A == B
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}
//源码来自:https://www.cnblogs.com/qdhxhz/p/12050701.html

示例

这里给出两种实现Predicate接口的方式——直接创建与lambda表达式:

public class TestFunction {
    public static void main(String[] args) {
            //1:直接创建Predicate接口
            Predicate<Integer> predicate = new Predicate<Integer>() {
                @Override
                public boolean test(Integer integer) {
                    if(integer > 10){
                        return true;
                    }
                    return false;
                }
            };

            System.out.println(predicate.test(6));
            System.out.println("********************");

            //2: 通过lambda表达式,
            predicate = (t) -> t > 5;
            System.out.println(predicate.test(1));
            System.out.println("********************");

        }
    }
//改写自:https://cloud.tencent.com/developer/article/1488128

结果为:

false
********************
false
********************

Process finished with exit code 0

总结

看了以上四个函数式接口的源码、示例,也许会有点疑惑——这些东西到底有啥用?感觉用处不大啊!

回答:为了进行函数式编程。关于函数式编程的好处和理念之后再细说,详情可参考

参考资料

https://cloud.tencent.com/developer/article/1488128

https://www.cnblogs.com/qdhxhz/p/12050701.html

https://www.cnblogs.com/qdhxhz/p/11323595.html

相关文章
|
2月前
|
Java 数据处理
|
3月前
|
设计模式 Java
结合HashMap与Java 8的Function和Optional消除ifelse判断
`shigen`是一位致力于记录成长、分享认知和留住感动的博客作者。本文通过具体代码示例探讨了如何优化业务代码中的if-else结构。首先展示了一个典型的if-else处理方法,并指出其弊端;然后引入了策略模式和工厂方法等优化方案,最终利用Java 8的Function和Optional特性简化代码。此外,还提到了其他几种消除if-else的方法,如switch-case、枚举行、SpringBoot的IOC等。一起跟随shigen的脚步,让每一天都有所不同!
43 10
结合HashMap与Java 8的Function和Optional消除ifelse判断
|
4月前
|
消息中间件 Java Kafka
"Kafka快速上手:从环境搭建到Java Producer与Consumer实战,轻松掌握分布式流处理平台"
【8月更文挑战第10天】Apache Kafka作为分布式流处理平台的领头羊,凭借其高吞吐量、可扩展性和容错性,在大数据处理、实时日志收集及消息队列领域表现卓越。初学者需掌握Kafka基本概念与操作。Kafka的核心组件包括Producer(生产者)、Broker(服务器)和Consumer(消费者)。Producer发送消息到Topic,Broker负责存储与转发,Consumer则读取这些消息。首先确保已安装Java和Kafka,并启动服务。接着可通过命令行创建Topic,并使用提供的Java API实现Producer发送消息和Consumer读取消息的功能。
90 8
|
4月前
|
消息中间件 Java 大数据
"深入理解Kafka单线程Consumer:核心参数配置、Java实现与实战指南"
【8月更文挑战第10天】在大数据领域,Apache Kafka以高吞吐和可扩展性成为主流数据流处理平台。Kafka的单线程Consumer因其实现简单且易于管理而在多种场景中受到欢迎。本文解析单线程Consumer的工作机制,强调其在错误处理和状态管理方面的优势,并通过详细参数说明及示例代码展示如何有效地使用KafkaConsumer类。了解这些内容将帮助开发者优化实时数据处理系统的性能与可靠性。
110 7
|
4月前
|
消息中间件 负载均衡 Java
"深入Kafka核心:探索高效灵活的Consumer机制,以Java示例展示数据流的优雅消费之道"
【8月更文挑战第10天】在大数据领域,Apache Kafka凭借其出色的性能成为消息传递与流处理的首选工具。Kafka Consumer作为关键组件,负责优雅地从集群中提取并处理数据。它支持消息的负载均衡与容错,通过Consumer Group实现消息的水平扩展。下面通过一个Java示例展示如何启动Consumer并消费数据,同时体现了Kafka Consumer设计的灵活性与高效性,使其成为复杂消费场景的理想选择。
141 4
|
4月前
|
安全 Oracle Java
【Azure Function】Azure Function中使用 Java 8 的安全性问题
【Azure Function】Azure Function中使用 Java 8 的安全性问题
|
5月前
|
机器学习/深度学习
现代深度学习框架构建问题之Sigmoid类实现Function接口如何解决
现代深度学习框架构建问题之Sigmoid类实现Function接口如何解决
37 4
|
6月前
|
Java
java使用Supplier接口的get生产一个数据
java使用Supplier接口的get生产一个数据
|
1天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者