Java 函數(shù)式編程實例

函數(shù)式編程概念

函數(shù)式編程是一種編程的范式和編程的方法論(programming paradigm)莺葫,它屬于結(jié)構(gòu)化編程的一種莺奸,主要的思想是把運算的過程盡量通過一組嵌套的函數(shù)來實現(xiàn)败徊。

函數(shù)式編程的幾個特點:

  • 函數(shù)可以作為變量、參數(shù)续滋、返回值和數(shù)據(jù)類型舅锄。
  • 基于表達式來替代方法的調(diào)用
  • 函數(shù)無狀態(tài)躯枢,可以并發(fā)和獨立使用
  • 函數(shù)無副作用则吟,不會修改外部的變量
  • 函數(shù)結(jié)果確定性;同樣的輸入闺金,必然會有同樣的結(jié)果逾滥。

函數(shù)式編程的優(yōu)點:

  • 代碼簡潔,開發(fā)效率高
  • 接近自然語言败匹,易于理解
  • 由于函數(shù)的特性寨昙,易于調(diào)試和使用
  • 易于并發(fā)使用
  • 腳本語言的特性,易于升級部署

@FunctionalInterface 函數(shù)式接口

@FunctionalInterface是 Java 8 新加入的一種接口掀亩,注解在接口層面舔哪,且注解的接口要有且僅有一個抽象方法。具體就是說槽棍,注解在Inteface上捉蚤,且interface里只能有一個抽象方法,可以有多個default方法炼七。

函數(shù)式接口的一大特性就是可以被lambda表達式和函數(shù)引用表達式代替

Lambda 表達式

Lambda 表達式是一種匿名函數(shù)(對 Java 而言這并不完全正確缆巧,但現(xiàn)在姑且這么認為),簡單地說豌拙,它是沒有聲明的方法陕悬,也即沒有訪問修飾符、返回值聲明和名字按傅。

你可以將其想做一種速記捉超,在你需要使用某個方法的地方寫上它胧卤。當某個方法只使用一次,而且定義很簡短拼岳,使用這種速記替代之尤其有效枝誊,這樣,你就不必在類中費力寫聲明與方法了惜纸。

使用場景

Redis工具類

JAVA是面向?qū)ο蟮囊度觯ǔ7椒ǖ娜雲(yún)⒍际穷悾▽ο螅蛘咦兞靠安荆瘮?shù)式編程痊乾,就是把一個函數(shù)(方法)作為入?yún)⑵け冢沁@個有啥好處呢椭更??

簡單舉個例子蛾魄,
當多個方法都有同樣的操作時虑瀑,我們通常想的是將其共同抽象成獨立方法,但是整個流程是一樣的滴须,只是不同場景下舌狗,具體業(yè)務處理處理不同時,我們該怎么抽象呢扔水?如果像下面那樣操作痛侍,明顯就是破壞了整個業(yè)務流程

   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();
    }

那想再不破壞整個流程的情況改怎么處理呢?可以利用函數(shù)式編程魔市,把接口作為入?yún)⒅鹘欤斁唧w業(yè)務處理時再去實現(xiàn)其具體業(yè)務。

@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将宪,或者線程池绘闷,都有一些通用的操作,首先较坛,先從池中取出對象印蔗,然后實現(xiàn)具體業(yè)務,然后再把對象放入池中丑勤;
可以看出這里有操作流程上有重復的地方华嘹,如果我們把這寫都寫在具體業(yè)務中,過于耦合和繁瑣确封,那我們就可以像上面的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);
        }

    }

    //使用匿名內(nèi)部類實現(xiàn)
    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表達式實現(xiàn)
    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表達式實現(xiàn)
    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();

}

抽象基類:把獲取鎖和釋放鎖抽象到寄類實現(xiàn)颜曾,在具體業(yè)務job不用關(guān)心這些

@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();
        // 控制并發(fā)鎖
        if (redisService.tryGetDistributedLock(taskName, requestId,600)) {
            long start = System.currentTimeMillis();

            try {
                // 開始執(zhí)行定時任務
                operation.execJob();
                logger.info("{}:執(zhí)行定時任務完成纠拔,耗時(毫秒):{}", taskName, (System.currentTimeMillis() - start));
            } catch (Exception e) {
                logger.error(taskName + ":執(zhí)行定時任務異常", e);
            } finally {
                // 釋放鎖
                try {
                    redisService.releaseDistributedLock(taskName,requestId);
                } catch (Exception e) {
                    logger.error(taskName + ":釋放鎖異常", e);
                }
            }
        } else {
            logger.info("{}:獲取鎖失敗", taskName);
        }
    }


    /**
     * 執(zhí)行JOB業(yè)務邏輯
     */
    public abstract void exec();

具體執(zhí)行任務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("=========");
    }
}

總結(jié):比較常用的,典型的應用場景泛豪,是當我們運算的過程可以抽象成好幾個步驟時稠诲,把其中相同部分,抽象成公共方法(像上面的common方法)诡曙,并且把函數(shù)式接口作為其入?yún)⑼涡穑诰唧w業(yè)務實現(xiàn)中,使用lambda表達式實現(xiàn)具體業(yè)務實現(xiàn)(像上面的method1Operation价卤、method2Operation)劝萤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市慎璧,隨后出現(xiàn)的幾起案子床嫌,更是在濱河造成了極大的恐慌,老刑警劉巖胸私,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厌处,死亡現(xiàn)場離奇詭異,居然都是意外死亡岁疼,警方通過查閱死者的電腦和手機阔涉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捷绒,“玉大人瑰排,你說我怎么就攤上這事「砑荩” “怎么了凶伙?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長它碎。 經(jīng)常有香客問我函荣,道長,這世上最難降的妖魔是什么扳肛? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任傻挂,我火速辦了婚禮,結(jié)果婚禮上挖息,老公的妹妹穿的比我還像新娘金拒。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布绪抛。 她就那樣靜靜地躺著资铡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪幢码。 梳的紋絲不亂的頭發(fā)上笤休,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天,我揣著相機與錄音症副,去河邊找鬼店雅。 笑死,一個胖子當著我的面吹牛贞铣,可吹牛的內(nèi)容都是我干的闹啦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼辕坝,長吁一口氣:“原來是場噩夢啊……” “哼窍奋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起圣勒,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤费变,失蹤者是張志新(化名)和其女友劉穎摧扇,沒想到半個月后圣贸,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡扛稽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年吁峻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片在张。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡用含,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出帮匾,到底是詐尸還是另有隱情啄骇,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布瘟斜,位于F島的核電站缸夹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏螺句。R本人自食惡果不足惜虽惭,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蛇尚。 院中可真熱鬧芽唇,春花似錦、人聲如沸取劫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至炮捧,卻和暖如春义辕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寓盗。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工灌砖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人傀蚌。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓基显,卻偏偏與公主長得像,于是被迫代替她去往敵國和親善炫。 傳聞我的和親對象是個殘疾皇子撩幽,可洞房花燭夜當晚...
    茶點故事閱讀 43,509評論 2 348