zoukankan      html  css  js  c++  java
  • Java8函数接口实现回调及Groovy闭包的代码示例

    本文适用于想要了解Java8 Function接口编程及闭包表达式的筒鞋。

    概述###

    在实际开发中,常常遇到使用模板模式的场景: 主体流程是不变的,变的只是其中要调用的具体方法。 其特征是:

      BeginTodo ---> Something different to do ---> others todo ---> End

    其中BeginTodo ,others todo,End 都是不变的,只有 Something different to do 是根据业务变化的。 如果采用 Java 来实现,通常要为 Something different to do 定义回调接口 Callback , 然后在主体流程中调用这个回调接口,而在实际业务中,创建 Callback 的实现类传入进去。

    Java8 Function 以及闭包对此提供了更为简洁方便的语言支持。 Function 是Java8对函数的抽象,用于描述和接收任何单参数单返回的函数,类似于 Callback 的作用; 而在使用的时候,只要将方法引用或 lambda 表达式传给 Function 即可。

    代码示例###

    举例来说,要编写一个通用方法,实现多次执行同一个测试方法,统计失败次数。

    Java-Function####

    package com.xxx.trade;
    
    import junit.framework.AssertionFailedError;
    import junit.framework.TestCase;
    import org.junit.Test;
    
    import java.util.function.Function;
    
    /**
     * Created by shuqin on 16/11/15.
     */
    public class GeneralExecTestMoreTimes extends TestCase {
    
        public void execMoreTimes(Function f) {
            execMoreTimes(f, new Object());
        }
    
        public void execMoreTimes(Function f, int num) {
            execMoreTimes(f, null, num);
        }
    
        public void execMoreTimes(Function f, Object t) {
            execMoreTimes(f, t, 20);
        }
    
        public void execMoreTimes(Function f, Object t, int num) {
            int failed = 0;
            for (int i = 0; i < num; i++) {
                try {
                    f.apply(t);
                } catch (AssertionFailedError afe) {
                    failed += 1;
                } catch (Exception ex) {
                    failed += 1;
                }
            }
            System.out.println("----- failed: " + failed + " -----");
            assertEquals(0, failed);
        }
    
        @Test
        public void testExecMoreTimes() {
            execMoreTimes((o) -> {
                System.out.println("haha");
                return 1;
            }, 5);
        }
    
    }
    

    这里 execMoreTimes 方法接收一个函数式接口 Function 以及将应用于的参数 t 。 那么什么可以传给 Function 接口呢? 可以是任何单参数单返回值的函数,或者单参数单返回值的 lambda 表达式。例如代码中的 (o) -> { // some codes } 。

    Groovy闭包####

    如果使用 Groovy 编写闭包,会更简洁: 只要定义一个由大括号 {} 包围的代码块,并赋给一个变量即可(如下面的test1),甚至可以直接在函数里调用一个由大括号包围的代码块参数(如下面的test2)。 这样, 使用代码块或函数就会更加直接、灵活、自由,而不会受制于语法,也不需要定义一堆接口了。

    package com.xxx.trade
    
    import org.junit.Test
    
    /**
     * Created by shuqin on 16/11/15.
     */
    class GeneralExecTestMoreTimesTest extends GeneralExecTestMoreTimes {
    
        @Test
        public void test1() {
            def closure = {
                System.out.println("here is test")
            }
            execMoreTimes(closure)
        }
    
    
        @Test
        public void test2() {
            execMoreTimes({
                throw new Exception("throw exception in test")
            })
        }
    }
    

    应用场景####

    函数接口可以用于任何使用回调接口的场景,一个典型的应用场景是批量处理。 比如有三个小组对同一个列表进行处理,一个用于状态同步,一个用于退款,一个用于取消。 那么,可以实现一个批量处理的通用函数,然后调用三个小组的自定义函数即可。

    package zzz.study.utils;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.function.Consumer;
    
    /**
     * Created by shuqin on 17/1/19.
     */
    public class BatchProcessUtil {
    
        public static String batchProcessOrders(Consumer<String> processFunction, List<String> orders) {
            StringBuilder result = new StringBuilder();
            for (String orderNo: orders) {
                String orderNoTrimed = orderNo.trim();
                try {
                    processFunction.accept(orderNo);
                    result.append(orderNoTrimed + " OK , 请稍后查看!
    ");
                } catch (Exception e) {
                    result.append(orderNoTrimed + " Failed, 请稍后重试!
    ");
                }
            }
            return result.toString();
        }
    
        public static void sync(String orderNo) {
            System.out.println("sync order state " + orderNo);
        }
    
        public static void refund(String orderNo) {
            System.out.println("refund for " + orderNo);
        }
    
        public static void cancel(String orderNo) {
            System.out.println("cancel " + orderNo);
        }
    
        public static void main(String[] args) {
            List<String> orders = Arrays.asList(new String[] {"E001", "E002", "E003"});
            batchProcessOrders((orderNo) -> sync(orderNo), orders);
            batchProcessOrders((orderNo) -> refund(orderNo), orders);
            batchProcessOrders((orderNo) -> cancel(orderNo), orders);
        }
    }
    

    输出是

    sync order state E001
    sync order state E002
    sync order state E003
    refund for E001
    refund for E002
    refund for E003
    cancel E001
    cancel E002
    cancel E003
    

    常用函数接口###

    常用函数接口主要有:

    1. Consumer (接收单参数无返回值的函数或lambda表达式), 方法是 void accept(T t);
    2. BiConsumer<T, U> (接收双参数无返回值的函数或 lambda表达式),方法是 void accept(T t, U u) ;
    3. Function<T, R> (接收单参数有返回值的函数或lambda表达式), 方法是 R apply(T t);
    4. BiFunction<T, U, R> (接收双参数有返回值的函数或lambda表达式),方法是 R apply(T t, U u);
    5. Predicate (接收单参数返回布尔值的函数或lambda表达式),方法是 boolean test(T t);
    6. Supplier (无参数返回值的函数或 lambda), 方法是 T get();
    7. 接受原子类型参数的函数接口,这里不一一列举了。可参考 java8 package java.util.function;

    小结###

    为什么要使用 Function 以及闭包呢?

    • 在语法上比定义回调接口、创建匿名类更加简洁;
    • 尝试使用新的语言特性,理解多样化的编程思想,提升编程表达能力。
  • 相关阅读:
    工作流调度器Azkaban的安装配置
    MySQL初学入门基础知识-sql语句
    spark大数据生态架构
    快速排序算法——分析及总结 (非常好)
    经典的大数据面试题总结
    flume采集数据报错问题解决
    haproxy官方文档
    问题
    2016/6/7学习记录
    2016
  • 原文地址:https://www.cnblogs.com/lovesqcc/p/6083759.html
Copyright © 2011-2022 走看看