• Java 8 Consumer、Supplier、Predicate、Function


    这几个接口都在 java.util.function 包下的,分别是Consumer(消费型)、supplier(供给型)、predicate(谓词型)、function(功能性)
    下面从具体的应用场景来讲讲这个接口的用法。

    Consumer接口

    源码:

    Consumer.java

    /**
     * 代表这样一种操作: 接收一个单值输入,没有返回值。与大部分函数式接口不同,
     *  Consumer一般通过"副作用"来操作。
     * Consumer 的函数式方法是accept
     * @since 1.8
     */
    @FunctionalInterface
    public interface Consumer<T> {
    
        /**
         * Performs this operation on the given argument.
         *
         * @param t the input argument
         */
        void accept(T t);
    
        /**
         * 这个默认方法与Function接口中的andThen方法类似,对于给定的入参after(类型也是Consumer),
         * 先执行上面的accept方法,再执行after中的accept方法。
         */
        default Consumer<T> andThen(Consumer<? super T> after) {
            Objects.requireNonNull(after);
            return (T t) -> { accept(t); after.accept(t); };
        }
    }
    

    使用Consumer接口的示例

         @Test
        public void test_Consumer(){
            //1. 使用consumer接口实现方法
            System.out.println("使用consumer接口实现方法");
            Consumer<String> consumer = new Consumer<String>() {
                @Override
                public void accept(String s) {
                    System.out.println(s);
                }
            };
            Stream<String> stream = Stream.of("aaa", "bbb");
            stream.forEach(consumer);
    
            System.out.println("*********************");
    
            //2. 使用lambda表达式, Stream的forEach方法需要的入参就是一个consumer接口
            System.out.println("使用lambda表达式");
            Consumer<String> consumer1 = (s) -> System.out.println(s);//lambda表达式返回的就是一个Consumer接口
            stream = Stream.of("aaa", "bbb");
            stream.forEach(consumer1);
            //更直接的方式
            //stream.forEach((s) -> System.out.println(s));
            System.out.println("********************");
    
            //3. 使用方法引用,方法引用也是一个consumer
            System.out.println("使用方法引用");
            Consumer consumer2 = System.out::println;
            stream = Stream.of("aaa", "bbb");
            stream.forEach(consumer2);
            //更直接的方式
            //stream.forEach(System.out::println);
            System.out.println("********************");
    
            //4. 演示Consumer的andThen方法
            Consumer<String> addHello = s -> System.out.println("hello");
            Consumer consumer3 = s -> System.out.println(s);
            //先执行consumer3定义的accept方法,再执行addHello定义的accept
            System.out.println("演示Consumer的andThen方法");
            stream = Stream.of("aaa", "bbb");
            stream.forEach(consumer3.andThen(addHello));
        }
    

    输出:

    使用consumer接口实现方法
    aaa
    bbb
    *********************
    使用lambda表达式
    aaa
    bbb
    ********************
    使用方法引用
    aaa
    bbb
    ********************
    演示Consumer的andThen方法
    aaa
    hello
    bbb
    hello
    

    说明:

    1. Consumer是一个接口,并且只要实现一个 accept 方法,就可以作为一个“消费者”输出信息。
    2. lambda 表达式、方法引用的返回值都是 Consumer 类型,所以,他们能够作为StreamforEach 方法的参数,并且输出一个值。

    其实除了StreamforEach方法, 我们常见的List列表的forEach方法也可以接收一个Consumer来遍历列表中的每个元素并对其进行Consumeraccept方法中定义的操作:
    java.lang.IterableforEach方法源码:

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    

    forEach的参数是一个Consumer,从forEach这个方法的定义来看,现在我们可以理解为“遍历我所包含的所有元素,对每个元素都执行一次action.accept()
    所以客户端可以这样使用ListforEach:

    List<String> list = new ArrayList<>();
    list.add("Hello ");
    list.add("World!");
    list.forEach(s -> System.out.println(s)); // 这里当然也可以用更为简洁的方法引用来改写
    

    ## Supplier 接口 #### 源码 > Supplier.java ``` @FunctionalInterface public interface Supplier {
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
    

    }

    `Supplier`接口是一个供给型的接口,其实,说白了就是一个容器,可以用来存储数据,然后可以供其他方法使用的这么一个接口。至于如何获取数据,交给用户去实现`Supplier`的`get`方法。
    #### 使用Supplier接口的示例
    

    @Test
    public void test_Supplier() {
    //① 使用Supplier接口实现方法,只有一个get方法,无参数,返回一个值
    Supplier supplier = new Supplier() {
    @Override
    public Integer get() {
    //返回一个随机值
    return new Random().nextInt();
    }
    };
    System.out.println("获取随机int");
    System.out.println(supplier.get());

        System.out.println("********************");
    
        //② 使用lambda表达式,
        supplier = () -> new Random().nextInt();
        System.out.println("使用lambda表达式定义supplier");
        System.out.println(supplier.get());
        System.out.println("********************");
    
        //③ 使用方法引用
        System.out.println("使用方法引用");
        Supplier<Double> supplier2 = Math::random;
        System.out.println(supplier2.get());
    
        Integer[] integers = {1, 2, 3, 4, 5};
        List<Integer> list = Arrays.asList(integers);
        Stream<Integer> stream = list.stream();
        //返回一个optional对象, optional对象可以持有元素或者null
        Optional<Integer> first = stream.filter(i -> i > 4)
                .findFirst();
        //optional对象有需要Supplier接口的方法
        //orElse,如果first中存在数,就返回这个数,如果不存在,就返回传入orElse中的数字
        System.out.println("使用optional对象找到大于4的第一个元素并返回,找不到返回-100");
        System.out.println(first.orElse(-100));
        System.out.println("********************");
    
        //这次找大于100的数字
        stream = list.stream();
        Optional<Integer> above100 = stream.filter(integer -> integer > 100).findFirst();
        Supplier<Integer> supplier3 = () -> new Random().nextInt();
    
        //orElseGet,如果first中存在数,就返回这个数,如果不存在,就返回supplier3返回的值
        System.out.println("orElseGet,如果first中存在数,就返回这个数,如果不存在,就返回supplier3返回的值");
        System.out.println(above100.orElseGet(supplier3));
    }
    
    输出:
    

    获取随机int
    -1992653641


    使用lambda表达式定义supplier
    -1661833099


    使用方法引用
    0.4916754644996516
    使用optional对象找到大于4的第一个元素并返回,找不到返回-100
    5


    orElseGet,如果first中存在数,就返回这个数,如果不存在,就返回supplier3返回的值
    -454437243

    说明:
    1. `Supplier` 接口可以理解为一个容器,用于装数据的。
    2. `Supplier` 接口有一个 `get` 方法,用户重写这个方法以提供返回。
    <br/>
    
    #### Supplier接口示例补充:
    > CallableLock.java
    

    public class CallableLock {
    public V lock(Supplier supplier){
    try {
    doLock();
    return supplier.get();
    }finally {
    unLock();
    }
    }

    private boolean doLock(){
        System.out.println("Lock!");
        return true;
    }
    
    private void unLock(){
        System.out.println("Unlock!");
    }
    

    }

    > Processor.java
    

    public class Processor {
    public boolean process() {
    System.out.println("Processing");
    try {
    Thread.sleep(10 * 1000);
    }catch (Exception e){
    e.printStackTrace();
    }
    return true;
    }
    }

    > LockTest.java
    

    public class LockTest {

    @Test
    public void lockTest(){
       CallableLock<Boolean> callableLock = new CallableLock<>();
       Processor processor = new Processor();
       callableLock.lock(processor::process);
        System.out.println("====================");
       callableLock.lock(()->processor.process());
        System.out.println("====================");
       callableLock.lock(new Supplier<Boolean>() {
           @Override
           public Boolean get() {
               return processor.process();
           }
       });
        System.out.println("====================");
    }
    

    }

    <br/>
    
    ## Predicate 接口
    #### 源码
    > Predicate.java
    

    @FunctionalInterface
    public interface Predicate {

    /**
     * 计算给定的boolean表达式的真假
     *
     * @param t 入参
     * @return {@code true} 根据输入t是否符合test函数中定义的判断逻辑返回true或false,
     */
    boolean test(T t);
     
    //下面在接口中默认实现的and方法、negate方法、or方法、isEqual方法, 可以实现嵌套判断
    //and方法提供逻辑与功能,具有短路效应
    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * AND of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code false}, then the {@code other}
     * predicate is not evaluated.
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
    
    //negate方法提供逻辑非功能
    /**
     * Returns a predicate that represents the logical negation of this
     * predicate.
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
    
    // or方法提供逻辑或功能,具有短路效应
    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * OR of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code true}, then the {@code other}
     * predicate is not evaluated.
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
    
    /** isEqual方法提供逻辑相等判断
     * Returns a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}.
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
    
    说明: 默认实现的`and`、 `negate`、 `or`、`isEqual`方法都返回`Predicate`对象。 用于嵌套的逻辑判断。
    #### Predicate接口示例
    
    @Test
    public void test_Predicate() {
        Integer[] integers = {1, 2, 3, 4, 5};
        List<Integer> list = Arrays.asList(integers);
        Stream<Integer> stream = list.stream();
        // 基本测试, 使用predicate打印所有大于3的数字
        Predicate<Integer> predicate = integer -> integer > 3;
        System.out.println("使用predicate打印所有大于3的数字");
        stream.filter(predicate).forEach((integer)-> System.out.println(integer));
        System.out.println("********************");
    
        //默认方法测试,这里只测试and方法, 其他的方法类似
        System.out.println("测试and方法, 使用两个predicate之间的and逻辑,打印所有大于3且为偶数的数字");
        Predicate<Integer> even = integer -> integer%2 == 0;
        stream = list.stream();
        stream.filter(predicate.and(even)).forEach(System.out::println);
    }
    
    测试结果:
    

    使用predicate打印所有大于3的数字
    4
    5


    测试and方法, 使用两个predicate之间的and逻辑,打印所有大于3且为偶数的数字

    说明:
    1. `Predicate` 是一个谓词型接口,其实只是起到一个判断作用。
    2. `Predicate` 通过实现一个 `test` 方法做判断。
    3. `Predicate`有默认实现的逻辑判断方法
    <br/>
    
    ## Function接口
    #### 基本使用
    见 [Java8 Function接口(apply compose andThen)](https://www.cnblogs.com/greatLong/articles/11975684.html)
    <br/>
    #### 链式调用进阶使用
    首先定义接口`ProcessUnit`继承`Function`接口并提供`apply`方法的默认实现:
    > ProcessUnit.java
    
    

    public interface ProcessUnit<T, R> extends Function<T, R> {
    @Override
    default R apply(T t){
    return process(t);
    }

    R process(T t);
    

    }

    这里对`apply`方法的默认实现是调用`ProcessUnit`定义的`process`方法,这个方法交给实现`ProcessUnit`的类去实现,从而可以在这里写自己的处理逻辑。在使用的时候可以使用`ProcessUnit`从`Function`接口中继承来的`compose`和`andThen`方法对逻辑处理进行组合。
    下面的`AndThen1`、`AndThen2`和`AndThen3`都分别实现了`ProcessUnit`接口,并重写了`process`方法来提供自己的处理逻辑。
    > AndThen1.java
    
    

    public class AndThen1 implements ProcessUnit<Integer, String>{
    @Override
    public String process(Integer integer) {
    integer += 1;
    System.out.println("I am in andThen1, the number is " + integer);
    String s = String.valueOf(integer);
    return s;
    }
    }

    > AndThen2.java
    
    

    public class AndThen2 implements ProcessUnit<String, String>{
    @Override
    public String process(String string) {
    Integer integer = Integer.parseInt(string) + 1;
    System.out.println("I am in andThen2, the number is " + integer);
    return String.valueOf(integer);
    }
    }

    > AndThen3.java
    
    

    public class AndThen3 implements ProcessUnit<String, String>{
    @Override
    public String process(String string) {
    Integer integer = Integer.parseInt(string) + 1;
    System.out.println("I am in andThen3, the number is " + integer);
    return String.valueOf(integer);
    }
    }

    测试方法:
    > test.java
    
    

    public class test {

    Function<Integer, String> function;
    
    @Before
    public void prepare(){
        System.out.println("Prepare the test ... ");
        System.out.println("++++++++++++++++++++++++++++++");
        function = new AndThen1().andThen(new AndThen2()).andThen(new AndThen3());
    }
    
    @Test
    public void testAndThen(){
        System.out.println(function.apply(100));
    }
    
    @After
    public void finish(){
        System.out.println("==============================");
        System.out.println("finish test");
    }
    

    }

    在测试类中定义了一个`Function`对象来接收方法调用链实例。
    测试结果:
    

    Prepare the test ...
    ++++++++++++++++++++++++++++++
    I am in andThen1, the number is 101
    I am in andThen2, the number is 102
    I am in andThen3, the number is 103
    103

    finish test

    Process finished with exit code 0

    
    > 参考文章
    >> [Java8之Consumer、Supplier、Predicate和Function攻略](https://www.cnblogs.com/SIHAIloveYAN/p/11288064.html)
  • 相关阅读:
    Docker安装
    Mysql 安全登陆工具 mysql_config_editor
    位图索引对于DML操作的影响
    删除Oracle Online Redo 测试
    16 Managing Undo
    Linux 不杀进程的情况下,如何释放磁盘资源
    SFTP 服务搭建
    8. DBNEWID 工具(使用nid命令修改db name及dbid)
    Null 值对索引排序的影响案例一则
    opensshd 源码升级
  • 原文地址:https://www.cnblogs.com/greatLong/p/11976821.html
Copyright © 2020-2023  润新知