Java 函数式编程实例

函数式编程概念

函数式编程是一种编程的范式和编程的方法论(programming paradigm),它属于结构化编程的一种,主要的思想是把运算的过程尽量通过一组嵌套的函数来实现

函数式编程的几个特点:

  • 函数可以作为变量、参数、返回值和数据类型。
  • 基于表达式来替代方法的调用
  • 函数无状态,可以并发和独立使用
  • 函数无副作用,不会修改外部的变量
  • 函数结果确定性;同样的输入,必然会有同样的结果。

函数式编程的优点:

  • 代码简洁,开发效率高
  • 接近自然语言,易于理解
  • 由于函数的特性,易于调试和使用
  • 易于并发使用
  • 脚本语言的特性,易于升级部署

@FunctionalInterface 函数式接口

@FunctionalInterface是 Java 8 新加入的一种接口,注解在接口层面,且注解的接口要有且仅有一个抽象方法。具体就是说,注解在Inteface上,且interface里只能有一个抽象方法,可以有多个default方法。

函数式接口的一大特性就是可以被lambda表达式和函数引用表达式代替

Lambda 表达式

Lambda 表达式是一种匿名函数(对 Java 而言这并不完全正确,但现在姑且这么认为),简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和名字。

你可以将其想做一种速记,在你需要使用某个方法的地方写上它。当某个方法只使用一次,而且定义很简短,使用这种速记替代之尤其有效,这样,你就不必在类中费力写声明与方法了。

使用场景

Redis工具类

JAVA是面向对象的,通常方法的入参都是类(对象),或者变量,而函数式编程,就是把一个函数(方法)作为入参,那这个有啥好处呢??

简单举个例子,
当多个方法都有同样的操作时,我们通常想的是将其共同抽象成独立方法,但是整个流程是一样的,只是不同场景下,具体业务处理处理不同时,我们该怎么抽象呢?如果像下面那样操作,明显就是破坏了整个业务流程

   public Object common1(){
        return "common1";
    }
    public Object common2(){
        return "common2";
    }
    

    public void method1(Object o){
        Object o1 =this.common1();
        //doSomeing
        System.out.println("========"+o1);
        this.common2();
    }


    public void method2(Object o){
        Object o1 =this.common1();
        //doSomeing
        System.out.println("-----------"+o1);
        this.common2();
    }

那想再不破坏整个流程的情况改怎么处理呢?可以利用函数式编程,把接口作为入参,当具体业务处理时再去实现其具体业务。

@FunctionalInterface
public interface Operation<T,R> {
    public T operate(R r);
}

    public void  common(Operation<Object,Object> operation){
        //step1
        Object o1 =this.common1();

        operation.operate(o1);
        //step3
        this.common2();
    }


    public void method1Operation(Object o){
        this.common(o1 -> "========"+o1);
    }


    public void method2Operation(Object o){
        this.common(o1 -> "========"+o1);
    }

上面的介绍过于抽象,下面介绍一个很实用的场景。
对于一些池的操作,比如redisPool,或者线程池,都有一些通用的操作,首先,先从池中取出对象,然后实现具体业务,然后再把对象放入池中;
可以看出这里有操作流程上有重复的地方,如果我们把这写都写在具体业务中,过于耦合和繁琐,那我们就可以像上面的demo一样,将其公用部分抽象出来,这里已redisPool为例,如下

@FunctionalInterface
public interface Operation<T,R> {

    public T operate(R r);

}

public class RedisTool2 {

    private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;
    //NX|XX, NX -- Only set the key if it does not already exist;
    //        XX -- Only set the key if it already exist.
    private static final String SET_IF_NOT_EXIST = "NX";
    //EX|PX, expire time units: EX = seconds; PX = milliseconds
    private static final String SET_WITH_EXPIRE_TIME = "PX";


    private static volatile JedisPool jedisPool = null;

    public static JedisPool getRedisPoolUtil() {
        if(null == jedisPool ){
            synchronized (RedisTool2.class){
                if(null == jedisPool){
                    GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
                    poolConfig.setMaxTotal(100);
                    poolConfig.setMaxIdle(10);
                    poolConfig.setMaxWaitMillis(100*1000);
                    poolConfig.setTestOnBorrow(true);
                    jedisPool = new JedisPool(poolConfig,"192.168.10.151",6379);
                }
            }
        }
        return jedisPool;
    }


    public static <T> T doOperation(Operation<T,Jedis> operation){
        Jedis  jedis = jedisPool.getResource();
        try {
            return operation.operate(jedis);
        }catch (Exception e){
            return null;
        }finally {
            jedisPool.returnResource(jedis);
        }

    }

    //使用匿名内部类实现
    public static boolean tryGetDistributedLock1(final String lockKey, final String requestId, final int expireTime) {
        return doOperation(new Operation<Boolean, Jedis>() {
            public Boolean operate(Jedis jedis) {
                String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

                if (LOCK_SUCCESS.equals(result)) {
                    return true;
                }
                return false;
            }
        });
    }

    //使用lambda表达式实现
    public static boolean tryGetDistributedLock2(final String lockKey, final String requestId, final int expireTime) {
        return doOperation(jedis ->{
            String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
            return false;
        });
    }

    //使用lambda表达式实现
    public static boolean tryGetDistributedLock2(final String lockKey, final String requestId, final int expireTime) {
        String result = doOperation(jedis ->jedis.set(lockKey, requestId, SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME, expireTime));
        return LOCK_SUCCESS.equals(result);
    }

   public boolean releaseDistributedLock(String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = this.execute(jedis ->jedis.eval(script, Collections.singletonList(COMMON_LOCK_KEY+lockKey), Collections.singletonList(requestId)));
        return RELEASE_SUCCESS.equals(result);
    }

    //普通方法
    public static boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
        Jedis  jedis = jedisPool.getResource();

        try {
            String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
            return false;
        }catch (Exception e){
            return false;
        }finally {
            jedisPool.returnResource(jedis);
        }

    }
}

分布式定时任务

@FunctionalInterface
public interface Operation {
    public void execJob();

}

抽象基类:把获取锁和释放锁抽象到寄类实现,在具体业务job不用关心这些

@Component
public  abstract class AbstractBasicTask {

    private static final Logger logger = LoggerFactory.getLogger(AbstractBasicTask.class);

    @Autowired
    RedisService redisService;

    public void doOperation(String taskName,Operation operation){
        String requestId = DateUtils.getNowTimeMill();
        // 控制并发锁
        if (redisService.tryGetDistributedLock(taskName, requestId,600)) {
            long start = System.currentTimeMillis();

            try {
                // 开始执行定时任务
                operation.execJob();
                logger.info("{}:执行定时任务完成,耗时(毫秒):{}", taskName, (System.currentTimeMillis() - start));
            } catch (Exception e) {
                logger.error(taskName + ":执行定时任务异常", e);
            } finally {
                // 释放锁
                try {
                    redisService.releaseDistributedLock(taskName,requestId);
                } catch (Exception e) {
                    logger.error(taskName + ":释放锁异常", e);
                }
            }
        } else {
            logger.info("{}:获取锁失败", taskName);
        }
    }


    /**
     * 执行JOB业务逻辑
     */
    public abstract void exec();

具体执行任务demoJob

@Component
@EnableScheduling
public class demoJob extends  AbstractBasicTask{
 
    @Scheduled(cron = "1 * * * * ?")
    @Override
    public void exec()  {
        this.doOperation("demoJob", this::testA);
    }


    private void testA(){
         System.out.println("=========");
    }
}

总结:比较常用的,典型的应用场景,是当我们运算的过程可以抽象成好几个步骤时,把其中相同部分,抽象成公共方法(像上面的common方法),并且把函数式接口作为其入参,在具体业务实现中,使用lambda表达式实现具体业务实现(像上面的method1Operation、method2Operation)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352