美文网首页编程语言-Java系列
Java 函数式编程实例

Java 函数式编程实例

作者: 雪飘千里 | 来源:发表于2020-10-27 16:08 被阅读0次

    函数式编程概念

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

    函数式编程的几个特点:

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

    函数式编程的优点:

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

    @FunctionalInterface 函数式接口

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

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

    Lambda 表达式

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

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

    使用场景

    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";
        //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;
            });
        }
    
    
        //普通方法
        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);
            }
    
        }
    }
    

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

    相关文章

      网友评论

        本文标题:Java 函数式编程实例

        本文链接:https://www.haomeiwen.com/subject/kbcxvktx.html